Run Bemmoly with Docker Compose
The one-command installer writes a Compose file and an .env, then runs docker compose up. If you already run Compose, use the same files yourself: four services, three of them optional, and one settings file.
These steps use the files of release 0.3.0. On a fresh VM the
one command does all of this for you, adds the
bemmoly command for backups and upgrades, and is the path
tested end to end.
What runs
| Service | Profile | What it is |
|---|---|---|
bemmoly | always | The app: API, web app and background jobs in one container. Listens on 8080, on loopback when the proxy runs. |
db | db | Postgres 18 with pgvector. Leave it out and set DATABASE_URL to use your own. |
proxy | proxy | Caddy with automatic HTTPS, HTTP/2 and HTTP/3 on ports 80 and 443. |
updater | updater | Runs updates and rollbacks from Settings › Updates. Holds the Docker socket, publishes no port. |
Every service is in one Compose project named bemmoly, on one network. The files
are in deploy/compose, each value read from
.env.
1. Put the files in /var/bemmoly
git clone --depth 1 --branch v0.3.0 https://github.com/bemmoly/bemmoly sudo install -d -m 755 /var/bemmoly sudo cp bemmoly/deploy/compose/docker-compose.yml bemmoly/deploy/compose/Caddyfile /var/bemmoly/ sudo install -m 600 bemmoly/deploy/compose/env.template /var/bemmoly/.env cd /var/bemmoly sudo install -d -m 750 -o 10001 -g 10001 data backups sudo install -d -m 755 pg sudo install -d -m 700 caddy caddy/data caddy/config
The app runs as user 10001 inside its container, so data and
backups belong to that id. Keep the folder at /var/bemmoly: the
updater works on the same path inside its container.
2. Fill in .env
Replace every @…@ placeholder in /var/bemmoly/.env. These are the
values the installer would write:
| Setting | Value |
|---|---|
VERSION, UPDATER_VERSION | The release to run, such as 0.3.0. |
COMPOSE_PROFILES | The optional services to start: db,proxy,updater for everything. |
BEMMOLY_DOMAIN | The hostname people will use, with a DNS record pointing here. |
BEMMOLY_TLS_MODE | auto once the domain points here; internal starts with a self-signed certificate. |
BEMMOLY_PUBLIC_URL | https:// and the domain. |
BEMMOLY_HTTP_BIND | 127.0.0.1:8080 behind the proxy; 0.0.0.0:8080 without it. |
BEMMOLY_DIR | The folder holding these files: /var/bemmoly. |
BEMMOLY_UPDATE_CHANNEL | stable or beta. |
POSTGRES_PASSWORD | A new password: openssl rand -hex 24. |
DATABASE_URL | postgres://bemmoly:PASSWORD@db:5432/bemmoly_db with the password above, or your own Postgres 18. |
PG_SHARED_BUFFERS, PG_EFFECTIVE_CACHE_SIZE | An eighth and three eighths of the memory: 512MB and 1536MB on 4 GB. |
BEMMOLY_SECRET_KEY | openssl rand -base64 32. Decrypts stored credentials; never change it later. |
BEMMOLY_BACKUP_PASSPHRASE | A long random string. Encrypts backups that leave the machine. |
UPDATER_TOKEN | openssl rand -hex 32, when the updater runs. |
BEMMOLY_UPDATER_URL | http://updater:8090 with the updater; empty without it. |
BEMMOLY_METRICS_TOKEN | Optional: a token for Prometheus scrapes of /metrics, which is off while it is empty. |
BEMMOLY_MODULES | Optional: pins the enabled modules. Empty lets an admin choose in Settings. |
Keep a copy of the finished file somewhere safe. Backups do not contain it, and a restored
workspace without its BEMMOLY_SECRET_KEY cannot read its stored credentials.
3. Start it
sudo docker compose up -d sudo docker compose ps The app waits for Postgres, applies its schema changes and starts; the first start can take a couple of minutes. Then open the domain to run the setup wizard and create the first admin.
Without the bundled Postgres or the proxy
- Your own Postgres: drop
dbfromCOMPOSE_PROFILESand pointDATABASE_URLat Postgres 18 (17 is accepted with a warning). Add the pgvector extension too: AI search needs it from 0.4. - Your own HTTPS: drop
proxy, setBEMMOLY_HTTP_BINDto0.0.0.0:8080and put your reverse proxy in front of port 8080. - No in-app updates: drop
updaterand leaveBEMMOLY_UPDATER_URLempty.
Updating
With the updater, update from Settings › Updates: it takes a backup, then swaps the version
and rolls back if the health check fails. By hand, take a backup in Settings › Storage and
backups, set VERSION and UPDATER_VERSION to the new release in
.env, then pull and restart; the app applies the new schema changes as it starts.
sudo docker compose pull sudo docker compose up -d The changelog lists what each release changes.