The image
RevFox is published to GitHub's container registry for linux/amd64 and linux/arm64. One container runs the whole server, with the panel built in.
ghcr.io/revfox-dev/revfox
| Tag | What it is |
|---|---|
latest | The newest release. |
0.3 | The newest patch release of 0.3. |
0.3.1 | Exactly that release. Never changes. |
edge | The newest unreleased commit. For testing, not production. |
sha-<commit> | One unreleased commit, by its short hash. |
The container listens on two ports:
| Port | Serves | Who calls it |
|---|---|---|
8080 | The panel and its /api/... endpoints | You and your team, in a browser |
8081 | /sdk-events, /v1/... and /app-store-server-notifications/... | Your apps, your backend, and the App Store |
Both answer GET /api/health with 204. The image's built-in health check uses it, and so can your platform.
Run with SQLite
The simplest setup: one container, with the database file in a volume at /app/data. Use this docker-compose.yml, also in the repository.
services:
revfox:
image: ghcr.io/revfox-dev/revfox:${REVFOX_IMAGE_TAG:-latest}
restart: unless-stopped
environment:
REVFOX_API_URL: ${REVFOX_API_URL:-}
REVFOX_ADMIN_PASSWORD: ${REVFOX_ADMIN_PASSWORD:-}
expose:
- "8080"
- "8081"
volumes:
- revfox-data:/app/data
volumes:
revfox-data:
On a deploy platform, create a compose-based service and paste the file in. The platform starts it and keeps the volume across deploys.
${...} values as they are
They are variables, not text to replace. ${REVFOX_API_URL:-} takes REVFOX_API_URL from the environment, or stays empty when it is not set. Most platforms list these variables in the service's environment settings, where you fill them in. See Configure.
On a plain Docker host
Without a platform in front, publish the ports yourself by replacing expose with:
ports:
- "8080:8080"
- "8081:8081"
docker compose up -d
Put a reverse proxy with TLS in front of both ports before exposing them to the internet, as in the Nginx example.
The server runs as user 10001. A named volume works as is. If you mount a host folder at /app/data instead, give that user ownership first: sudo chown -R 10001:10001 /path/to/folder.
Run with PostgreSQL
For a database you can back up, inspect and move separately, run PostgreSQL next to RevFox. Use docker-compose.postgres.yml:
services:
revfox:
image: ghcr.io/revfox-dev/revfox:${REVFOX_IMAGE_TAG:-latest}
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
environment:
REVFOX_API_URL: ${REVFOX_API_URL:-}
REVFOX_ADMIN_PASSWORD: ${REVFOX_ADMIN_PASSWORD:-}
REVFOX_DATABASE_TYPE: postgres
REVFOX_DATABASE_HOST: postgres
REVFOX_DATABASE_NAME: revfox
REVFOX_DATABASE_USER: revfox
REVFOX_DATABASE_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD}
expose:
- "8080"
- "8081"
postgres:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_DB: revfox
POSTGRES_USER: revfox
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD}
volumes:
- postgres-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U revfox -d revfox"]
interval: 5s
timeout: 5s
retries: 10
volumes:
postgres-data:
Set POSTGRES_PASSWORD to a long random value; the file refuses to start without it. The database is only reachable from inside the compose network.
An existing database
To use a PostgreSQL server you already run, drop the postgres service and give RevFox a connection URL instead:
services:
revfox:
image: ghcr.io/revfox-dev/revfox:${REVFOX_IMAGE_TAG:-latest}
restart: unless-stopped
environment:
REVFOX_API_URL: ${REVFOX_API_URL:-}
REVFOX_ADMIN_PASSWORD: ${REVFOX_ADMIN_PASSWORD:-}
REVFOX_DATABASE_TYPE: postgres
REVFOX_DATABASE_URL: ${REVFOX_DATABASE_URL:?set REVFOX_DATABASE_URL}
expose:
- "8080"
- "8081"
Give RevFox its own database and user rather than sharing the default postgres database. As a superuser on that server:
CREATE USER revfox WITH PASSWORD 'long-random-password';
CREATE DATABASE revfox OWNER revfox;
RevFox creates its tables in the empty database on its first start.
A database on another server
For a managed database or one on another machine, use its public host and require TLS:
REVFOX_DATABASE_URL=postgres://revfox:[email protected]:5432/revfox?sslmode=require
A database on the same server
A database your platform already runs on the same server is reachable by its container name, over a Docker network the two containers share. Two things to set:
- The host. Use the database's container name, not
localhost. Platforms show it in the database's internal connection URL, such aspostgres://postgres:…@k4d9x2:5432/postgres; the host isk4d9x2. - The network. A compose stack gets its own network, so it cannot see other containers by default. Turn on your platform's setting that connects the stack to its shared or predefined network.
REVFOX_DATABASE_URL=postgres://revfox:long-random-password@k4d9x2:5432/revfox?sslmode=disable
sslmode=disable is fine here: the traffic stays on the server's internal Docker network. Use require if the database has TLS turned on.
On a plain Docker host, attach RevFox to the database's network yourself. docker inspect <container> lists the networks a container is on:
services:
revfox:
# ...as above
networks:
- default
- database
networks:
database:
external: true
name: the-database-network
RevFox cannot resolve the database's container name, so the two containers are not on a shared network. Check the network step above.
Add the domains
Give each port its own domain. In your platform's domain settings for the revfox service, route:
| Domain | Container port |
|---|---|
https://app.example.com | 8080 |
https://api.example.com | 8081 |
Some platforms take both in one field, with the container port after the domain: https://app.example.com:8080,https://api.example.com:8081. The port there picks the container port; visitors still use 443.
Then set REVFOX_API_URL to the API domain and redeploy. The panel shows this URL wherever you copy an API address, such as the App Store server notification URL.
REVFOX_API_URL=https://api.example.com
The proxy needs no extra configuration. RevFox reads the standard Host, X-Forwarded-Proto, X-Forwarded-Host and X-Forwarded-For headers that platform proxies send by default, to mark the session cookie secure, check where panel requests come from, and record client IPs.
Your apps, your backend and Apple must reach the API domain from anywhere. The panel domain is only for your team, so you can put an IP allowlist or an access proxy in front of it.
Configure
The compose files read their settings from variables written as ${...}. Leave the file as it is and set the values outside it:
- On a deploy platform, in the service's environment settings. Most platforms read the compose file and list its variables there for you.
- On a plain Docker host, in a
.envfile next to the compose file.docker composereads it by itself.
REVFOX_IMAGE_TAG=0.3.1
REVFOX_API_URL=https://api.example.com
REVFOX_ADMIN_PASSWORD=a-long-random-password
Each variable is written in one of three ways:
| In the file | When the variable is not set |
|---|---|
${REVFOX_API_URL:-} | Empty. RevFox then uses its own default. |
${REVFOX_IMAGE_TAG:-latest} | The value after :-, here latest. |
${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD} | Required: the deploy stops with that message. |
| Variable | Purpose |
|---|---|
REVFOX_IMAGE_TAG | The image tag to run. Defaults to latest; pin a version such as 0.3.1 in production. |
REVFOX_API_URL | The public URL of the API domain. Empty, the panel derives it from the address in your browser. |
REVFOX_ADMIN_PASSWORD | The first admin's password, read only when the database is new. Empty, a random one is printed in the log. |
POSTGRES_PASSWORD | The PostgreSQL password, in the PostgreSQL setup. |
REVFOX_DATABASE_URL | The connection URL, when you use an existing database. |
REVFOX_LOG_LEVEL | debug, info, warn or error. Defaults to info. |
Every other setting, such as the database connection and the Play notification polling, has an environment variable too. See Configuration.
First login
A fresh database creates a bootstrap account, [email protected]. Unless you set REVFOX_ADMIN_PASSWORD, its password is random and printed once in the container log. Open your platform's log view, or run:
docker compose logs revfox | grep "first admin account"
Sign in and change the bootstrap password before exposing the panel publicly. If you set REVFOX_ADMIN_PASSWORD, you can remove it after the first start.
Upgrade
Change REVFOX_IMAGE_TAG to the new version and redeploy. On a plain Docker host:
docker compose pull
docker compose up -d
RevFox updates its database schema when it starts. Back up first, and read the release notes for anything to do by hand.
Back up
Many platforms can back up volumes and databases on a schedule; turn that on for the volume at /app/data or for the PostgreSQL service. To back up by hand:
SQLite
Stop RevFox so the database file is not mid-write, archive the volume, then start it again.
docker compose stop revfox
docker run --rm --volumes-from "$(docker compose ps -aq revfox)" -v "$PWD":/backup alpine \
tar czf /backup/revfox-data.tgz -C /app/data .
docker compose start revfox
PostgreSQL
docker compose -f docker-compose.postgres.yml exec postgres \
pg_dump -U revfox revfox > revfox.sql
