Docker Compose
Auf einem einzelnen Server ist Docker Compose der einfachste Weg. Alles, was Sie dafür brauchen, steht auf dieser Seite: drei Dateien und Ihr Deploy-Token.
1. Bei der Registry anmelden
Melden Sie den Server mit dem Deploy-Token an, das Sie von uns bekommen haben:
docker login registry.gitlab.com -u <Name des Tokens> -p <Token>
2. Die Dateien ablegen
Legen Sie einen Ordner an, zum Beispiel /opt/ddm, und darin diese drei Dateien.
compose.yaml – die vier Dienste, ihre Volumes und Health-Checks. Sie bleibt, wie sie ist; was Sie einstellen, steht in .env.
# DDM on one server, with Docker Compose — as the user guide describes it
# („Selbst betreiben" / „Self-hosting").
#
# cp .env.example .env # and fill it in
# docker login registry.gitlab.com # with the deploy token you were sent
# docker compose up -d
#
# Four services: Postgres, the cms (the management app, and the API everything
# else talks to), the render worker (text, thumbnails and PDFs of what is
# uploaded) and the reader app. The cms and the reader app answer on two
# hostnames, behind a reverse proxy that terminates TLS: your own, pointed at
# the ports below, or Caddy from this file (`docker compose --profile proxy up
# -d`, see `Caddyfile`).
#
# Every setting of the user guide's „Konfiguration" / „Configuration" can go
# into `.env`; the ones set below are fixed by this layout. The images are those of
# the release in `DDM_VERSION`; the cms migrates the database when it starts.
name: ddm
x-app: &app
restart: unless-stopped
# Followed by Watchtower started with `--label-enable`, for an installation
# that updates itself (`DDM_VERSION=stable`). Not Postgres: a new major of it
# needs its data upgraded by hand.
labels:
com.centurylinklabs.watchtower.enable: "true"
services:
postgres:
# Postgres with the German dictionary DDM searches with — by its major,
# which only ever changes by hand.
image: ${DDM_REGISTRY:-registry.gitlab.com/iq-me/new-ddm-images}/postgres:18
restart: unless-stopped
environment:
POSTGRES_USER: ddm
POSTGRES_DB: ddm
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}
volumes:
- postgres:/var/lib/postgresql
healthcheck:
test: [CMD, pg_isready, -U, ddm, -d, ddm]
interval: 5s
timeout: 5s
retries: 10
cms:
<<: *app
image: ${DDM_REGISTRY:-registry.gitlab.com/iq-me/new-ddm-images}/cms:${DDM_VERSION:-latest}
env_file: .env
environment:
# Which version of these files this is: a release that needs newer ones
# says so to the administrators, rather than failing (user guide, „Updates").
DDM_DEPLOY_VERSION: "1"
DATABASE_URL: postgres://ddm:${POSTGRES_PASSWORD}@postgres:5432/ddm
BETTER_AUTH_URL: https://${DDM_CMS_HOST:?set DDM_CMS_HOST in .env}
CLIENT_ORIGIN: https://${DDM_APP_HOST:?set DDM_APP_HOST in .env}
# The documents, and the database's dumps taken before each migration
# (the last three) — unless `S3_ENDPOINT` names a bucket for both.
STORAGE_DIR: /data/storage
BACKUP_DIR: /data/backups
# Uploads are stored here and drawn by the render worker.
RENDER_MODE: worker
MAX_UPLOAD_BYTES: ${MAX_UPLOAD_BYTES:-1073741824}
volumes:
- data:/data
ports:
- ${DDM_CMS_PORT:-127.0.0.1:3000}:3000
depends_on:
postgres:
condition: service_healthy
healthcheck:
test: [CMD, bun, -e, "process.exit((await fetch('http://127.0.0.1:3000/api/auth/ok')).ok ? 0 : 1)"]
interval: 10s
timeout: 5s
retries: 6
# Migrations first, on a new release.
start_period: 5m
start_interval: 2s
render-worker:
<<: *app
image: ${DDM_REGISTRY:-registry.gitlab.com/iq-me/new-ddm-images}/render-worker:${DDM_VERSION:-latest}
environment:
DATABASE_URL: postgres://ddm:${POSTGRES_PASSWORD}@postgres:5432/ddm
STORAGE_DRIVER: ${STORAGE_DRIVER:-}
STORAGE_DIR: /data/storage
S3_ENDPOINT: ${S3_ENDPOINT:-}
S3_BUCKET: ${S3_BUCKET:-}
S3_REGION: ${S3_REGION:-}
S3_ACCESS_KEY_ID: ${S3_ACCESS_KEY_ID:-}
S3_SECRET_ACCESS_KEY: ${S3_SECRET_ACCESS_KEY:-}
S3_FORCE_PATH_STYLE: ${S3_FORCE_PATH_STYLE:-true}
THUMBNAILS: ${THUMBNAILS:-on}
RENDER_LARGE_OFFICE: ${RENDER_LARGE_OFFICE:-on}
volumes:
- data:/data
# After the cms has migrated the database to this release.
depends_on:
cms:
condition: service_healthy
# It finishes the documents in hand before it stops — LibreOffice may take
# minutes over a large deck.
stop_grace_period: 11m
client:
<<: *app
image: ${DDM_REGISTRY:-registry.gitlab.com/iq-me/new-ddm-images}/client:${DDM_VERSION:-latest}
environment:
NUXT_API_ORIGIN: http://cms:3000
NUXT_PUBLIC_MGMT_URL: https://${DDM_CMS_HOST}
# The reader app on this installation, for the mobile app's „Server".
NUXT_PUBLIC_SERVER_URL: https://${DDM_APP_HOST}
RENDER_MODE: worker
THUMBNAILS: ${THUMBNAILS:-on}
NUXT_PUBLIC_SSO_PROVIDERS: ${NUXT_PUBLIC_SSO_PROVIDERS:-}
ports:
- ${DDM_APP_PORT:-127.0.0.1:3001}:3000
depends_on:
cms:
condition: service_healthy
# Through to the cms: what the reader app forwards answers, too.
healthcheck:
test: [CMD, bun, -e, "process.exit((await fetch('http://127.0.0.1:3000/api/auth/ok')).ok ? 0 : 1)"]
interval: 10s
timeout: 5s
retries: 6
start_period: 1m
start_interval: 2s
# TLS and both hostnames, with certificates from Let's Encrypt: only with
# `--profile proxy`, for a server with nothing else on ports 80 and 443.
caddy:
image: caddy:2.10-alpine
profiles: [proxy]
restart: unless-stopped
environment:
DDM_CMS_HOST: ${DDM_CMS_HOST}
DDM_APP_HOST: ${DDM_APP_HOST}
ports:
- 80:80
- 443:443
- 443:443/udp
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy:/data
depends_on: [cms, client]
volumes:
postgres:
data:
caddy:
.env – Ihre Einstellungen. Was Sie mindestens ausfüllen, steht im nächsten Schritt.
# An installation's settings, read by `compose.yaml` — copied to `.env` and
# filled in. Every setting of the user guide's „Konfiguration" /
# „Configuration" may be added; these are the ones an installation needs, and
# the ones most change.
# The release to run, e.g. `0.26.0`, or `0.26` for its fixes as they come.
# `stable` follows the releases we have promoted — for an installation that
# updates itself (user guide, „Updates").
DDM_VERSION=latest
# DDM_REGISTRY=registry.gitlab.com/iq-me/new-ddm-images
# The two hostnames, each with its own certificate: the management app (and
# API), and the reader app. DNS for both points at this server.
DDM_CMS_HOST=cms.example.com
DDM_APP_HOST=app.example.com
# Their parent, so a sign-in on one counts on the other.
AUTH_COOKIE_DOMAIN=example.com
# …and a passkey in both apps.
PASSKEY_RP_ID=example.com
# Where the two apps answer on this server, for your own reverse proxy —
# unused with `--profile proxy`.
# DDM_CMS_PORT=127.0.0.1:3000
# DDM_APP_PORT=127.0.0.1:3001
# Secrets: `openssl rand -base64 32` for each. The database's password is
# kept by Postgres on its first start; changing it here later locks the cms out.
POSTGRES_PASSWORD=
BETTER_AUTH_SECRET=
# The licence we sent you (`ddm1.…`) — needed from the first start: without
# one, the installation is locked, and nobody can register its first account.
# A licence entered later under Settings › Licence replaces it.
LICENCE_KEY=
# How mail leaves the installation — your own mail server.
MAIL_DRIVER=smtp
MAIL_FROM=ddm@example.com
SMTP_HOST=mail.example.com
SMTP_PORT=587
SMTP_USER=
SMTP_PASSWORD=
# SMTP_ALLOW_SELF_SIGNED=true # for an internal server with its own CA
# The documents are kept on the `data` volume. To keep them in an
# S3-compatible bucket instead (MinIO, Ceph, a cloud's), name it here; the
# database's dumps then go to the same bucket.
# S3_ENDPOINT=https://s3.example.com
# S3_BUCKET=ddm
# S3_REGION=eu-central-1
# S3_ACCESS_KEY_ID=
# S3_SECRET_ACCESS_KEY=
# Thumbnails and previews of uploaded documents: `off` leaves them out.
# THUMBNAILS=on
Caddyfile – nur für den mitgelieferten Reverse Proxy (Schritt 4).
# The reverse proxy of `compose.yaml`'s `proxy` profile: TLS for both
# hostnames, with certificates Caddy fetches itself — the server has to be
# reachable on ports 80 and 443 under both names. Caddy sets no limit on a
# request's size, so uploads up to `MAX_UPLOAD_BYTES` pass; another proxy needs
# it raised (nginx: `client_max_body_size 1g`).
{$DDM_CMS_HOST} {
encode zstd gzip
reverse_proxy cms:3000
}
{$DDM_APP_HOST} {
encode zstd gzip
reverse_proxy client:3000
}
3. Die Einstellungen ausfüllen
Tragen Sie in .env mindestens ein:
DDM_VERSION=0.26 # die Version; 0.26 bekommt ihre Fehlerbehebungen
DDM_CMS_HOST=cms.firma.de
DDM_APP_HOST=app.firma.de
AUTH_COOKIE_DOMAIN=firma.de
PASSKEY_RP_ID=firma.de
POSTGRES_PASSWORD=... # openssl rand -base64 32
BETTER_AUTH_SECRET=... # openssl rand -base64 32
LICENCE_KEY=ddm1.... # die Lizenz von uns
MAIL_DRIVER=smtp
MAIL_FROM=ddm@firma.de
SMTP_HOST=mail.firma.de
SMTP_PORT=587
SMTP_USER=...
SMTP_PASSWORD=...
LICENCE_KEYist von Anfang an nötig. Ohne Lizenz startet die Installation gesperrt, und niemand kann das erste Konto anlegen.POSTGRES_PASSWORDübernimmt Postgres beim ersten Start. Ändern Sie es danach nicht mehr in.env– das CMS käme sonst nicht mehr an die Datenbank.AUTH_COOKIE_DOMAINundPASSKEY_RP_IDsind die gemeinsame Domain beider Hostnamen. So gelten eine Anmeldung und ein Passkey im CMS auch in der Leser-App.
Alle weiteren Einstellungen stehen unter Konfiguration.
4. Den Reverse Proxy wählen
CMS und Leser-App antworten auf dem Server nur lokal, auf 127.0.0.1:3000 und 127.0.0.1:3001. Davor gehört ein Reverse Proxy, der TLS übernimmt:
- Der mitgelieferte Caddy, wenn auf den Ports 80 und 443 sonst nichts läuft. Er holt die Zertifikate für beide Hostnamen selbst. Starten Sie dann mit
--profile proxy. - Ihr eigener Proxy (nginx, Traefik, ein Load Balancer). Leiten Sie
DDM_CMS_HOSTauf Port 3000 undDDM_APP_HOSTauf Port 3001. Erlauben Sie Anfragen bis 1 GB, sonst scheitern große Uploads – bei nginx mitclient_max_body_size 1g;.
5. Starten
docker compose up -d # mit eigenem Proxy
docker compose --profile proxy up -d # mit Caddy
Beim ersten Start legt das CMS die Datenbank an. Das dauert eine Minute; docker compose ps zeigt die Dienste danach als healthy.
6. Das erste Konto anlegen
Öffnen Sie https://cms.firma.de und registrieren Sie sich – mit Ihrem Namen, Ihrer Firma und Ihrer E-Mail-Adresse. Sie legen damit Ihre Organisation an und sind ihr Inhaber. Danach laden Sie Ihre Kollegen über Benutzer ein oder binden Ihr Verzeichnis unter Einstellungen › Verzeichnisdienste an.
Wie viele Organisationen Ihre Installation haben darf, steht in der Lizenz. Ist die Zahl erreicht, kann sich niemand mehr eine neue anlegen.
Die Daten
| Volume | Inhalt |
|---|---|
ddm_postgres | Die Datenbank. |
ddm_data | Die Dokumente (storage/) und die Sicherungen der Datenbank (backups/). |
ddm_caddy | Die Zertifikate des mitgelieferten Proxys. |
Sollen die Dokumente in einem S3-Bucket liegen, tragen Sie ihn unter S3_ENDPOINT, S3_BUCKET und den Schlüsseln ein. Die Sicherungen landen dann im selben Bucket.