Deploy in 5 minutes

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

On the server
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

In /var/bemmoly
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 db from COMPOSE_PROFILES and point DATABASE_URL at 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, set BEMMOLY_HTTP_BIND to 0.0.0.0:8080 and put your reverse proxy in front of port 8080.
  • No in-app updates: drop updater and leave BEMMOLY_UPDATER_URL empty.

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.

In /var/bemmoly
sudo docker compose pull sudo docker compose up -d

The changelog lists what each release changes.