Strapi mit Docker installieren: Führendes Open-Source Headless CMS selbst hosten
Strapi 5 erzeugt aus Inhaltsmodellen automatisch REST- und GraphQL-Schnittstellen. Diese Anleitung zeigt den Aufbau mit Docker Compose und PostgreSQL, die Pflicht-Secrets und die Absicherung für den Produktivbetrieb.
Neue Version verfügbar: strapi 5.57.0. Diese Anleitung wird überarbeitet. Beschrieben ist strapi 5.56.0.
WerbelinksMit * markierte Links sind Werbelinks: Bei einem Kauf erhalten wir eine Provision, der Preis bleibt gleich. Als Amazon-Partner verdiene ich an qualifizierten Verkäufen. Mehr dazu

Strapi ist ein Open-Source Headless CMS: Es verwaltet Inhalte und liefert sie per API aus, die Darstellung übernimmt eine separate Website oder App. Sie definieren Inhaltstypen im Browser, Strapi erzeugt daraus REST- und auf Wunsch GraphQL-Schnittstellen. Diese Anleitung zeigt, wie Sie Strapi 5 mit Docker Compose und PostgreSQL auf einem Linux-Host betreiben, die Pflicht-Secrets erzeugen und den Dienst für den Produktivbetrieb absichern.
Voraussetzungen
- Docker Engine >= 20.10 und das Docker Compose Plugin v2 (
docker compose, nicht das veraltetedocker-compose) auf einem Linux-Host (x86-64 oder ARM64), einer VM oder einem NAS mit Docker-Unterstützung, 2 CPU-Kerne empfohlen. Installation siehe Docker und Docker Compose auf Linux installieren. - Node.js 20 oder neuer (Strapi 5 unterstützt laut
engines-Angabe Version 20 bis 26, empfohlen eine LTS-Version wie 22) auf dem Rechner, auf dem Sie das Projekt anlegen. Nur fürnpx create-strapi-appnötig. - Mindestens 2 GB freier Arbeitsspeicher für den Docker-Build-Prozess (node-gyp, Sharp-Kompilierung) und 5 GB freier Speicherplatz (node_modules, Docker-Images, Datenbank).
- openssl für die Secrets (auf gängigen Linux-Hosts vorinstalliert).
- Für den Produktivbetrieb: ein Reverse Proxy (Nginx, Caddy, Traefik) mit TLS, siehe Caddy als Reverse Proxy einrichten.
| Eigenschaft | Wert |
|---|---|
| Aktuelle Version | v5.56.0 (30.09.2026) |
| Docker-Image | Self-Build (node:22-alpine), kein offizielles Image |
| Datenbank (empfohlen) | PostgreSQL 16 |
| Port | 1337 (HTTP, Admin-Panel + API) |
| RAM (Minimum) | 2 GB (Build), 512 MB (Betrieb) |
| Lizenz | MIT (Community Edition) |
Schritt 1: Strapi-Projekt anlegen und Projektordner vorbereiten
Strapi braucht einen Projektordner mit package.json, package-lock.json und src/, den das offizielle CLI anlegt. Führen Sie auf Ihrem Rechner oder direkt auf dem Server aus:
npx create-strapi-app@latest mein-strapi --no-run
Wählen Sie im Assistenten PostgreSQL als Datenbank. --no-run verhindert den lokalen Start, Strapi läuft nur in Docker.
Wechseln Sie in den Projektordner und legen Sie die Unterordner an:
cd mein-strapi
mkdir -p public/uploads config
Die Datei .dockerignore hält lokale node_modules aus dem Build heraus:
cat <<'EOF' > .dockerignore
node_modules/
.tmp/
.cache/
.git/
build/
.env
EOF
Erstellen Sie das Dockerfile. Es lehnt sich an das Community-Beispiel der Strapi-Dokumentation an und nutzt node:22-alpine:
cat <<'EOF' > Dockerfile
FROM node:22-alpine AS base
# Abhängigkeiten für Sharp (Bildverarbeitung) und node-gyp
RUN apk add --no-cache \
build-base \
python3 \
vips-dev
ARG NODE_ENV=development
ENV NODE_ENV=${NODE_ENV}
WORKDIR /opt/app
COPY package.json package-lock.json ./
RUN npm install --legacy-peer-deps
ENV PATH=/opt/app/node_modules/.bin:$PATH
WORKDIR /opt/app
COPY . .
RUN chown -R node:node /opt/app
USER node
EXPOSE 1337
CMD ["npm", "run", "develop"]
EOF
Verifizieren: Alle Pflichtdateien liegen im Projektordner:
ls -la
# Erwartete Ausgabe (Auszug):
# -rw-r--r-- 1 user user ... Dockerfile
# -rw-r--r-- 1 user user ... .dockerignore
# -rw-r--r-- 1 user user ... package.json
# -rw-r--r-- 1 user user ... package-lock.json
# drwxr-xr-x 2 user user ... config/
# drwxr-xr-x 3 user user ... public/
# drwxr-xr-x 4 user user ... src/
Schritt 2: Pflicht-Secrets generieren und .env anlegen
Strapi startet nicht, solange die Schlüssel Platzhalter enthalten. Erzeugen Sie die Werte einzeln mit openssl:
# Jeden Befehl einzeln ausführen und den Wert notieren
openssl rand -base64 32 # APP_KEY_1
openssl rand -base64 32 # APP_KEY_2
openssl rand -base64 32 # ADMIN_JWT_SECRET
openssl rand -base64 32 # JWT_SECRET
openssl rand -base64 32 # API_TOKEN_SALT
openssl rand -base64 32 # TRANSFER_TOKEN_SALT
openssl rand -base64 32 # ENCRYPTION_KEY
openssl rand -base64 32 # DATABASE_PASSWORD
Legen Sie die .env an und ersetzen Sie alle Platzhalter in Großbuchstaben durch Ihre Werte:
# Strapi Server
HOST=0.0.0.0
PORT=1337
NODE_ENV=development
# Datenbank (PostgreSQL)
DATABASE_CLIENT=postgres
DATABASE_HOST=strapiDB
DATABASE_PORT=5432
DATABASE_NAME=strapi
DATABASE_USERNAME=strapi
DATABASE_PASSWORD=IHR_SICHERES_PASSWORT
# Pflicht-Secrets (alle 6 individuell generieren!)
APP_KEYS=IHR_APP_KEY_1,IHR_APP_KEY_2
ADMIN_JWT_SECRET=IHR_ADMIN_JWT_SECRET
JWT_SECRET=IHR_JWT_SECRET
API_TOKEN_SALT=IHR_API_TOKEN_SALT
TRANSFER_TOKEN_SALT=IHR_TRANSFER_TOKEN_SALT
ENCRYPTION_KEY=IHR_ENCRYPTION_KEY
Hinweis zu APP_KEYS: Der Wert ist eine kommagetrennte Liste, tragen Sie mindestens zwei Schlüssel ein. Vorlagen für Strapi 4 fehlt oft ENCRYPTION_KEY, der ab Strapi 5 Pflicht ist.
Verifizieren: Die .env enthält keinen Platzhalter mehr:
grep -c "IHR_\|toBeModified\|SICHERES" .env
# Erwartete Ausgabe: 0 (kein Platzhalter mehr vorhanden)
Schritt 3: compose.yaml und Datenbankkonfiguration erstellen
Legen Sie die compose.yaml im Projektordner an. Sie definiert strapi (Self-Build) und strapiDB (PostgreSQL 16) in einem gemeinsamen Netzwerk. Konfiguration, Quellcode und Uploads liegen als Bind-Mounts im Projektordner und überstehen Neustarts. Der PostgreSQL-Port ist nur an localhost gebunden:
services:
strapi:
container_name: strapi
build: .
image: strapi:latest
restart: unless-stopped
env_file: .env
environment:
NODE_ENV: development
volumes:
- ./config:/opt/app/config
- ./src:/opt/app/src
- ./.env:/opt/app/.env
- ./public/uploads:/opt/app/public/uploads
ports:
- "1337:1337"
networks:
- strapi
depends_on:
strapiDB:
condition: service_healthy
strapiDB:
container_name: strapiDB
restart: unless-stopped
image: postgres:16-alpine
environment:
POSTGRES_USER: ${DATABASE_USERNAME}
POSTGRES_PASSWORD: ${DATABASE_PASSWORD}
POSTGRES_DB: ${DATABASE_NAME}
volumes:
- strapi-data:/var/lib/postgresql/data/
ports:
- "127.0.0.1:5432:5432"
networks:
- strapi
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DATABASE_USERNAME} -d ${DATABASE_NAME}"]
interval: 10s
timeout: 5s
retries: 5
volumes:
strapi-data:
networks:
strapi:
name: strapi
driver: bridge
DATABASE_HOST=strapiDB muss exakt dem Service-Namen entsprechen. Im Container zeigt localhost auf den Strapi-Container selbst, nicht auf die Datenbank.
Damit nach längerer Inaktivität keine Verbindungsfehler auftreten, setzen Sie den Connection-Pool auf min: 0. Erstellen Sie config/database.js:
cat <<'EOF' > config/database.js
module.exports = ({ env }) => ({
connection: {
client: env('DATABASE_CLIENT', 'postgres'),
connection: {
host: env('DATABASE_HOST', 'strapiDB'),
port: env.int('DATABASE_PORT', 5432),
database: env('DATABASE_NAME', 'strapi'),
user: env('DATABASE_USERNAME', 'strapi'),
password: env('DATABASE_PASSWORD', ''),
ssl: env.bool('DATABASE_SSL', false),
},
pool: {
min: 0, // Pflicht in Docker: verhindert KnexTimeoutError nach Inaktivität
max: 10,
},
debug: false,
},
});
EOF
Verifizieren: Die Compose-Syntax ist fehlerfrei:
docker compose config --quiet
# Keine Ausgabe = valide YAML-Syntax
Schritt 4: Container bauen und starten
Der erste Build dauert 10 bis 15 Minuten, weil native Abhängigkeiten wie die Bildbibliothek Sharp kompiliert werden. Folgende Builds nutzen den Cache:
docker compose up -d --build
Verfolgen Sie die Logs:
docker compose logs -f strapi
Strapi ist bereit, sobald diese Zeilen erscheinen:
# Erwartete Ausgabe (Auszug):
# Project information
# ...
# Welcome back!
# To manage your project, go to the administration panel at:
# http://0.0.0.0:1337/admin
Verifizieren:
docker compose ps
# NAME IMAGE COMMAND STATUS PORTS
# strapi strapi:latest ... Up 2 minutes 0.0.0.0:1337->1337/tcp
# strapiDB postgres:16-… ... Up 3 minutes (healthy) 127.0.0.1:5432->5432/tcp
curl -I http://localhost:1337
# HTTP/1.1 302 Found (Weiterleitung zur /admin-Anmeldeseite)
Schritt 5: Admin-Account erstellen und Admin-Panel einrichten
Öffnen Sie http://localhost:1337/admin (auf einem entfernten Server http://SERVER-IP:1337/admin). Beim ersten Aufruf registrieren Sie den ersten Administrator mit Vorname, Nachname, E-Mail und Passwort. Legen Sie dieses Konto sofort an, solange der Port nur Ihnen zugänglich ist, sonst kann ein Dritter es übernehmen.
Im Content-Type Builder (linke Seitenleiste) legen Sie Ihre Inhaltsmodelle an. Strapi erzeugt die passenden REST-Endpunkte, etwa /api/artikel für den Inhaltstyp „Artikel“. GraphQL steht nach Installation des GraphQL-Plugins (Marketplace → GraphQL) unter /graphql zur Verfügung.
Verifizieren: Nach dem Login antwortet die API:
curl http://localhost:1337/_health
# HTTP 204 No Content (Strapi ist erreichbar)
curl -s http://localhost:1337/api | head -c 100
# {"data":null,"error":{"status":403,...}}
# HTTP 403 ist korrekt, alle Endpunkte sind standardmäßig geschützt
Schritt 6: Produktivbetrieb absichern
Für den Produktivbetrieb sind drei Änderungen Pflicht:
1. NODE_ENV auf production setzen: Ändern Sie NODE_ENV in der .env und im Block environment: der compose.yaml auf production. Der Eintrag in der compose.yaml überschreibt die .env, eine Änderung nur dort bliebe wirkungslos. Im Dockerfile ergänzen Sie vor EXPOSE die Zeile RUN ["npm", "run", "build"] und ändern CMD auf ["npm", "run", "start"]. Ohne Build findet npm run start kein Admin-Panel.
2. Port auf Loopback beschränken: Ändern Sie die Port-Bindung des Strapi-Services:
ports:
- "127.0.0.1:1337:1337"
Strapi ist dann nur noch für einen lokalen Reverse Proxy erreichbar. Den PostgreSQL-Port entfernen Sie in Produktion ganz.
3. Reverse Proxy mit TLS vorschalten: Richten Sie Nginx, Caddy oder Traefik vor Strapi ein, für Traefik siehe Traefik als Docker-Reverse-Proxy mit automatischem HTTPS. Nach allen Änderungen Image neu bauen:
docker compose build --no-cache
docker compose up -d
Verifizieren:
docker compose ps
# Alle Services: STATUS = Up
# In Produktion darf kein direkter Internetzugriff möglich sein:
# curl http://IHRE-SERVER-IP:1337 → Connection refused
Schritt 7: Updates und Backups
Für ein Update passen Sie die Strapi-Paketversionen in der package.json an (oder nutzen npx @strapi/upgrade minor) und bauen das Image neu. Sichern Sie vorher die Datenbank:
# Datenbank-Backup vor dem Update
docker exec strapiDB pg_dump -U strapi strapi > strapi_backup_$(date +%Y%m%d).sql
# Image neu bauen und Container neu starten
docker compose build --no-cache
docker compose up -d
Automatische Sicherung mit Rotation beschreibt MySQL & PostgreSQL Backup automatisieren mit cron. Sichern Sie außerdem ./src, ./config und ./public/uploads: Dort liegen Inhaltstypen und Medien.
Verifizieren:
docker compose ps
# Alle Services wieder Up nach dem Neustart
docker compose logs strapi --tail=20
# Keine Fehler zu Datenbank-Verbindung oder fehlenden Secrets
Troubleshooting / Typische Fehler
- ECONNREFUSED oder ENOTFOUND localhost:
DATABASE_HOSTin der.envist auflocalhoststatt auf den Compose-Service-Namen (strapiDB) gesetzt. Lösung:DATABASE_HOST=strapiDB. - KnexTimeoutError: Knex: Timeout acquiring a connection: Der Connection-Pool hat
min > 0. Lösung: inconfig/database.jspool.minauf0setzen. - Admin-Panel: „Invalid token“ oder „Forbidden“: Eines der sechs Secrets enthält noch einen Platzhalter. Alle sechs (
APP_KEYS,ADMIN_JWT_SECRET,JWT_SECRET,API_TOKEN_SALT,TRANSFER_TOKEN_SALT,ENCRYPTION_KEY) brauchen zufällige Werte. - Build-Fehler „vips-dev not found“ oder Sharp-Kompilierungsfehler:
vips-devfehlt in derapk add-Zeile des Dockerfiles. Hilft das nicht, empfiehlt die Strapi-Dokumentationnode:22-slimstatt Alpine. - „Database already exists“ beim ersten Start: Das PostgreSQL-Volume enthält Daten eines früheren Versuchs. Lösung bei einer Neuinstallation:
docker compose down -v(löscht alle Volumes), dann neu starten. - „ENCRYPTION_KEY is required“: Ab Strapi 5 Pflicht, in Vorlagen für Strapi 4 fehlt der Wert. Ergänzen und neu starten.
- Upload-Dateien nach Container-Neustart verloren: Der Mount
./public/uploads:/opt/app/public/uploadsfehlt in dercompose.yaml. - ARM64/Apple Silicon langsam:
platform: linux/amd64im Compose erzwingt x86-Emulation. Weglassen,node:22-alpineläuft auf ARM64 nativ.
Häufige Fragen
Brauche ich zwingend PostgreSQL oder geht auch SQLite?
SQLite genügt für lokale Tests. Für den Produktivbetrieb ist PostgreSQL die bessere Wahl: SQLite verarbeitet Schreibzugriffe nur nacheinander und erlaubt keine zweite Strapi-Instanz auf derselben Datenbank.
Wie generiere ich sichere Werte für APP_KEYS, JWT_SECRET und Co.?
Mit openssl rand -base64 32 für jeden Wert oder mit Node.js: node -e "console.log(require('crypto').randomBytes(32).toString('base64'))". Für APP_KEYS mindestens zwei kommagetrennte Strings eintragen, z.B. APP_KEYS=wert1,wert2.
Kann ich Strapi mit einer bestehenden Datenbank verbinden?
Nein. Strapi ist nicht für bestehende Datenbanken ausgelegt, die Datenbank muss beim ersten Start leer sein. Strapi legt die Tabellen selbst an.
Wo sind Content-Types nach einem Container-Neustart?
Die Definitionen liegen als Dateien unter ./src. Mit dem Mount ./src:/opt/app/src bleiben sie erhalten, ohne ihn gehen sie beim Neuerstellen des Containers verloren.
Gibt es eine fertige Alternative zum Self-Build?
Es gibt Community-Images, aber keine offiziellen: Strapi kennzeichnet die Dockerfile-Beispiele der Dokumentation ausdrücklich als Gefälligkeit für die Community. vshadbolt/strapi wird für Strapi 5 gepflegt (Stand 01.10.2026: 5.55.1, AMD64 und ARM64). naskio/strapi endet bei 5.30.1 (letzte Aktualisierung November 2025). Prüfen Sie bei Fremd-Images Herkunft und Aktualität selbst.
Wie aktualisiere ich Strapi auf eine neue Version?
Wie in Schritt 7: Backup von Datenbank und ./src, Paketversionen anpassen, docker compose build --no-cache und docker compose up -d. Updates können Schema-Migrationen enthalten, lesen Sie vorher die Release Notes.
Kann ich Strapi hinter Nginx oder Traefik betreiben?
Ja, das ist der empfohlene Aufbau: Strapi an 127.0.0.1:1337 binden, TLS im Reverse Proxy terminieren. Die Strapi-Dokumentation enthält Beispiele für Nginx, Apache und Caddy.
Fazit
Strapi 5 eignet sich für Teams, die ihr Content-Backend selbst betreiben wollen. Der Aufbau mit Docker Compose und PostgreSQL verlangt mehr Vorbereitung als ein einzelnes docker run: Projekt per CLI anlegen, sechs Secrets erzeugen, Connection-Pool anpassen und für den Produktivbetrieb Build, Port-Bindung und Reverse Proxy einrichten. Danach entstehen neue Inhaltstypen samt API ohne Programmieraufwand.
Weiterführende Anleitungen und Quellen
- Docker Compose: Multi-Container-Stacks aufbauen – Compose-Syntax, Netzwerke und Volumes verständlich erklärt
- Docker-Netzwerke und Volumes richtig nutzen – warum
localhostim Container eine andere Adresse ist - Docker Compose absichern: Secrets, Healthchecks, Non-Root – Strapi im Produktivbetrieb härten
- PostgreSQL pg_dump und pg_restore: Backup und Migration – Strapi-Datenbank sichern und wiederherstellen
Offizielle Quellen: Strapi Docker-Installationsdokumentation (docs.strapi.io) | Strapi GitHub-Repository | Strapi Datenbankkonfiguration


