Docker Compose
The repository has two compose files under deploy/: one that runs ramzes with its built-in SQLite
database, and one that adds Postgres. Both build the image from the checkout. There is no published
image to pull.
With SQLite
Section titled “With SQLite”From the repository root:
docker compose -f deploy/compose.yaml up --buildThen open http://localhost:7269. Add -d to run it in the background.
The file defines one service and one volume:
services: ramzes: build: context: .. dockerfile: deploy/Dockerfile ports: - "7269:7269" volumes: - ramzes-data:/data restart: unless-stopped
volumes: ramzes-data:What the image is
Section titled “What the image is”deploy/Dockerfile builds in three stages: the web UI with Node 24, the binary with Rust 1.98 (the
UI is embedded in it), and a debian:bookworm-slim runtime image with CA certificates.
| User | ramzes, uid 10001, not root |
| Entry point | ramzes |
| Environment | RAMZES_LISTEN=0.0.0.0:7269, RAMZES_DATA_DIR=/data |
| Volume | /data |
| Port | 7269 |
| Health check | GET /healthz every 10 seconds |
To build the image on its own:
docker build -f deploy/Dockerfile -t ramzes .The data volume and the secret key
Section titled “The data volume and the secret key”/data holds two things:
ramzes.db(with SQLite’s-waland-shmfiles beside it): everything ramzes stores.secret.key: the key that encrypts stored connection credentials. ramzes generates it on first start when you do not supply one.
Keep the volume. If you remove it (docker compose down -v), the database and the key are gone.
To supply the key yourself instead of keeping it in the volume, generate one and pass it in the environment:
openssl rand -base64 32services: ramzes: environment: RAMZES_SECRET_KEY: "<the 44 characters printed above>"When RAMZES_SECRET_KEY is set, ramzes does not read or create secret.key. Set it before the
first start, or copy the value from the existing secret.key; changing the key later makes the
credentials already stored unreadable.
With Postgres
Section titled “With Postgres”docker compose -f deploy/compose.postgres.yaml up --buildThis file runs postgres:18 next to ramzes and points ramzes at it:
services: ramzes: environment: RAMZES_DATABASE_URL: postgres://ramzes:ramzes@postgres:5432/ramzes volumes: # Still needed: the generated secret key lives here. - ramzes-data:/data depends_on: postgres: condition: service_healthy
postgres: image: postgres:18 environment: POSTGRES_USER: ramzes POSTGRES_PASSWORD: ramzes POSTGRES_DB: ramzes volumes: - postgres-data:/var/lib/postgresqlBefore you use it for anything real:
- Change the password. It appears in two places:
POSTGRES_PASSWORDandRAMZES_DATABASE_URL. - Keep both volumes.
postgres-dataholds the database;ramzes-datastill holdssecret.keyunless you setRAMZES_SECRET_KEY.
ramzes creates its tables itself when it starts. To use a Postgres server you already run, take the
SQLite compose file and add RAMZES_DATABASE_URL with your server’s address.
Changing settings
Section titled “Changing settings”Every setting is an environment variable; add it under environment:. The full list is in the
Configuration reference. For example:
services: ramzes: environment: RAMZES_PUBLIC_URL: https://ramzes.example.com RAMZES_SECURE_COOKIES: "true" RAMZES_METRICS_TOKEN: "<a long random string>" RAMZES_LOG_FORMAT: jsonTo run a subcommand in the running container, use exec. This writes a backup of the SQLite
database into the volume and copies it out:
docker compose -f deploy/compose.yaml exec ramzes ramzes backup /data/backup.dbdocker compose -f deploy/compose.yaml cp ramzes:/data/backup.db ./backup.dbdocker compose -f deploy/compose.yaml cp ramzes:/data/secret.key ./secret.keySee Backup, export and import.
Behind a reverse proxy with HTTPS
Section titled “Behind a reverse proxy with HTTPS”ramzes speaks plain HTTP and does not terminate TLS. To serve it over HTTPS, put a reverse proxy in front and forward everything to port 7269.
-
Forward all paths. The UI, the admin API (
/api/...), the ingest endpoints (/v1/...), the acknowledgement page (/ack/...) and the health and metrics endpoints are all on the same port. -
Set
RAMZES_SECURE_COOKIES=true. The session cookie is then markedSecure, so the browser sends it only over HTTPS. -
Set the public address. Set
RAMZES_PUBLIC_URL=https://ramzes.example.com, or type the address under Settings. Without it, messages carry no link back to ramzes and no acknowledgement link. -
Do not buffer
/api/stream. The UI gets live updates over server-sent events on that path, with a keep-alive every 15 seconds. A proxy that buffers responses delays them. -
Do not publish port 7269 as well. Remove the
ports:entry, or bind it to the loopback interface ("127.0.0.1:7269:7269"), so the only way in is through the proxy. -
Decide who may reach
/metrics. It is open unlessRAMZES_METRICS_TOKENis set. Set a token, or have the proxy refuse the path from outside.
A minimal Caddy configuration, with Caddy on the same host and ramzes bound to loopback:
ramzes.example.com { reverse_proxy 127.0.0.1:7269}ramzes does not read X-Forwarded-For or similar headers, and it sends no CORS headers.
Stopping and updating
Section titled “Stopping and updating”docker compose stop sends SIGTERM. ramzes stops accepting connections, waits up to 3 seconds
for open ones (the UI’s live streams never end on their own), gives deliveries that are in flight
up to 20 seconds to finish, and exits. A delivery that did not finish is attempted again after the
next start, so its message may arrive twice.
Docker kills a container that has not exited 10 seconds after SIGTERM unless told otherwise. Both
compose files set stop_grace_period: 30s, so ramzes gets its full time.
To update, pull the repository and build again:
git pulldocker compose -f deploy/compose.yaml up --build -dRead Upgrading first: database migrations run at start and there is no way back except a backup.