Plane mit Docker installieren: Self-hosted Projektmanagement als Jira-Alternative
Plane Community Edition v1.4.2 mit den offiziellen Release-Dateien per Docker Compose betreiben: Passwörter samt Verbindungs-URLs ändern, Proxy nur lokal, Instanz-Administrator, Reverse Proxy, getestetes Datenbank-Backup und Update.
Geprüft am 30.09.2026 · für plane 1.4.2
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

Plane ist ein Projektmanagement-Werkzeug mit Arbeitspaketen (Work Items), Zyklen, Modulen, Seiten und Kanban-, Listen- und Gantt-Ansichten. Die Community Edition steht unter AGPL-3.0 und lässt sich auf eigener Hardware betreiben. Diese Anleitung zeigt den offiziellen Weg für Docker Compose mit Plane v1.4.2: Dateien direkt aus dem Release laden, Standardpasswörter ersetzen, Ports nur lokal veröffentlichen, Instanz-Administrator anlegen, Reverse Proxy, Backup und Update.
Voraussetzungen
- Server: 2 CPU-Kerne (x86_64 oder ARM64) und laut Herstellerdoku mindestens 4 GB RAM, für den Produktivbetrieb empfiehlt Plane 8 GB. Im Test belegten alle Container im Leerlauf zusammen rund 1,3 GB RAM.
- Speicher: mindestens 20 GB frei. Die Images belegen zusammen mehrere Gigabyte, dazu kommen Datenbank und Uploads in MinIO.
- Software: Linux (Debian, Ubuntu) mit Docker Engine und aktuellem Compose-Plugin.
- Netz: ein DNS-Name wie
plane.example.comund ein Reverse Proxy mit TLS, alternativ das integrierte HTTPS des Plane-Proxys. - Edition: Diese Anleitung beschreibt die Community Edition. Die Herstellerdoku stellt inzwischen die Commercial Edition mit dem Installer
prime-cliin den Vordergrund, die Community Edition wird weiter über die Release-Dateien auf GitHub verteilt.
| Eckdaten | Wert |
|---|---|
| Version | Plane v1.4.2 (Tag v1.4.2 auf Docker Hub, makeplane/*) |
| Anwendungsdienste | web, space, admin, live, api, worker, beat-worker, migrator, proxy |
| Infrastruktur | postgres:15.7-alpine, valkey/valkey:7.2.11-alpine, rabbitmq:3.13.6-management-alpine, MinIO (pgsty/minio) |
| Ports | Proxy 80 und 443, per Override an 127.0.0.1 |
| Volumes | pgdata, redisdata, uploads, rabbitmq_data, proxy_config, proxy_data, vier Log-Volumes |
| Pflichtänderungen | SECRET_KEY, LIVE_SERVER_SECRET_KEY, POSTGRES_PASSWORD, RABBITMQ_PASSWORD, AWS_SECRET_ACCESS_KEY |
Schritt 1: Release-Dateien laden
Das offizielle Skript setup.sh lädt genau zwei Dateien aus dem Release und ruft danach docker compose mit --env-file plane.env auf. Sie können das Skript nutzen oder denselben Ablauf von Hand ausführen. Der manuelle Weg zeigt, was passiert, und legt die Version fest: Die Datei aus dem Release enthält v1.4.2 als Standard-Tag, die Datei im Hauptzweig dagegen stable.
sudo mkdir -p /opt/plane-app && cd /opt/plane-app
R=https://github.com/makeplane/plane/releases/download/v1.4.2
sudo curl -fsSLo docker-compose.yaml $R/docker-compose.yml
sudo curl -fsSLo plane.env $R/variables.env
sudo chmod 600 plane.env
Verifizieren: grep -c 'APP_RELEASE:-v1.4.2' docker-compose.yaml liefert 9. ls -l plane.env zeigt -rw-------.
Schritt 2: plane.env anpassen
Die Vorlage enthält Standardwerte wie SECRET_KEY=change-this-key-on-deployment, POSTGRES_PASSWORD=plane und für MinIO secret-key. Ersetzen Sie alle. Die Kommentare der Vorlage nennen openssl rand -hex 32 als Erzeuger.
cd /opt/plane-app
set_env() { sudo sed -i "s|^$1=.*|$1=$2|" plane.env; }
PG=$(openssl rand -hex 16); MQ=$(openssl rand -hex 16)
set_env APP_RELEASE v1.4.2
set_env APP_DOMAIN plane.example.com
set_env WEB_URL https://plane.example.com
set_env CORS_ALLOWED_ORIGINS https://plane.example.com
set_env LISTEN_HTTP_PORT 8081
set_env LISTEN_HTTPS_PORT 8444
set_env SECRET_KEY "$(openssl rand -hex 32)"
set_env LIVE_SERVER_SECRET_KEY "$(openssl rand -hex 32)"
set_env AWS_SECRET_ACCESS_KEY "$(openssl rand -hex 24)"
set_env POSTGRES_PASSWORD "$PG"
set_env DATABASE_URL "postgresql://plane:$PG@plane-db/plane"
set_env RABBITMQ_PASSWORD "$MQ"
set_env AMQP_URL "amqp://plane:$MQ@plane-mq:5672/plane"
Der Grund für die beiden URL-Zeilen: In der Compose-Datei steht DATABASE_URL: ${DATABASE_URL:-postgresql://plane:plane@plane-db/plane}, die Vorlage lässt DATABASE_URL leer. Ändern Sie nur POSTGRES_PASSWORD, legt PostgreSQL den Benutzer mit dem neuen Passwort an, API und Migrator versuchen es aber weiter mit plane. Gleiches gilt für RabbitMQ und AMQP_URL.
SITE_ADDRESS=:80 bleibt für den Betrieb hinter einem eigenen Reverse Proxy stehen. Wer den Plane-Proxy direkt ins Internet stellt, trägt dort den Domainnamen und in CERT_EMAIL eine Adresse ein, dann holt der integrierte Caddy ein Let's-Encrypt-Zertifikat.
Verifizieren: grep -E '^(SECRET_KEY|POSTGRES_PASSWORD|RABBITMQ_PASSWORD|AWS_SECRET_ACCESS_KEY)=' plane.env zeigt keine Standardwerte mehr. docker compose --env-file plane.env config | grep -E 'DATABASE_URL|AMQP_URL' | sort -u zeigt URLs mit Ihren Passwörtern.
Schritt 3: Ports nur lokal veröffentlichen
Der Dienst proxy veröffentlicht seine Ports ohne Angabe einer Adresse, also auf allen Schnittstellen. Statt die Herstellerdatei zu ändern, legen Sie eine compose.override.yaml an. Compose liest sie automatisch mit, der Tag !override ersetzt die Portliste vollständig statt sie zu ergänzen:
services:
proxy:
ports: !override
- "127.0.0.1:${LISTEN_HTTP_PORT:-8081}:80"
- "127.0.0.1:${LISTEN_HTTPS_PORT:-8444}:443"
Hinweis: Die Override-Datei wird nur gelesen, wenn Sie docker compose im Projektordner ohne eigenes -f aufrufen. Das Skript setup.sh ruft Compose mit -f docker-compose.yaml auf und ignoriert sie deshalb. Arbeiten Sie nach dieser Anleitung durchgehend mit docker compose --env-file plane.env.
Verifizieren: docker compose --env-file plane.env config | grep -B1 -A3 host_ip zeigt für beide Ports host_ip: 127.0.0.1.
Schritt 4: Stack starten
cd /opt/plane-app
docker compose --env-file plane.env pull
docker compose --env-file plane.env up -d
docker compose --env-file plane.env ps -a --format 'table {{.Service}}\t{{.Status}}'
docker compose --env-file plane.env logs migrator
Der Dienst migrator legt das Datenbankschema an und beendet sich danach mit Exit-Code 0. Alle übrigen Dienste laufen dauerhaft.
Im Test dauerte der Download aller Images rund 45 Sekunden. Der migrator lief gut 2 Minuten und endete mit Zeilen wie Applying sessions.0001_initial... OK, danach lieferte die Oberfläche HTTP 200.
Verifizieren: migrator steht auf Exited (0), alle anderen Dienste auf Up. curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8081/ liefert 200, curl -s http://127.0.0.1:8081/api/instances/ liefert JSON mit "is_setup_done": false.
Schritt 5: Instanz-Administrator anlegen
Öffnen Sie https://plane.example.com/god-mode/ und legen Sie das Konto des Instanz-Administrators an. Solange das nicht geschehen ist, kann jeder, der die Adresse erreicht, diese Rolle übernehmen. Richten Sie die Instanz deshalb ein, bevor der Proxy sie öffentlich macht, zum Beispiel über einen SSH-Tunnel auf Port 8081.
Im Bereich God Mode legen Sie danach fest:
- Email: SMTP-Server für Einladungen und Passwort-Zurücksetzen.
- Authentication: Die Option „Allow anyone to sign up without an invite“ schalten Sie aus, wenn nur eingeladene Personen Zugang erhalten sollen.
- General: Die Übermittlung anonymer Nutzungsdaten an Plane lässt sich hier abschalten.
Im Test setzte die Registrierung des ersten Administrators is_setup_done auf true. Ein zweiter Versuch wurde mit der Weiterleitung auf /god-mode/?error_code=5150&error_message=ADMIN_ALREADY_EXIST abgewiesen.
Verifizieren: curl -s http://127.0.0.1:8081/api/instances/ | grep -o '"is_setup_done":[a-z]*' liefert "is_setup_done":true. Nach der Anmeldung können Sie einen Workspace und ein erstes Projekt anlegen.
Schritt 6: Reverse Proxy mit HTTPS
Die Herstellerdoku für externe Proxys verlangt SITE_ADDRESS=:80 und freie Ports für den Plane-Proxy, beides ist nach Schritt 2 und 3 gesetzt. Nginx leitet auf den lokalen Port weiter, mit WebSocket-Unterstützung für den Echtzeit-Editor (live):
server {
listen 443 ssl;
http2 on;
server_name plane.example.com;
ssl_certificate /etc/letsencrypt/live/plane.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/plane.example.com/privkey.pem;
client_max_body_size 10M;
location / {
proxy_pass http://127.0.0.1:8081/;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
client_max_body_size passt zu FILE_SIZE_LIMIT=5242880 (5 MB) aus der Vorlage. Wer größere Anhänge erlaubt, erhöht beide Werte. Hintergründe zu Proxy und Zertifikaten finden Sie in Nginx als Reverse Proxy mit TLS und Traefik als Docker-Reverse-Proxy.
Verifizieren: sudo nginx -t meldet syntax is ok. curl -sI https://plane.example.com/ liefert HTTP/2 200. Zwei Personen bearbeiten gleichzeitig eine Seite und sehen die Änderungen des anderen ohne Neuladen.
Schritt 7: Backup und Update
Die Backup-Funktion von setup.sh (Menüpunkt 7) kopiert die Datenverzeichnisse von PostgreSQL, MinIO, RabbitMQ und Valkey aus den laufenden Containern per docker cp. Eine Kopie des PostgreSQL-Verzeichnisses im laufenden Betrieb ist nicht garantiert konsistent. Sichern Sie die Datenbank deshalb zusätzlich als Dump, dazu die Uploads und die plane.env:
cd /opt/plane-app && mkdir -p backup
docker compose --env-file plane.env exec -T plane-db pg_dump -h /var/run/postgresql -U plane -Fc plane > backup/plane-db.dump
docker run --rm -v plane-app_uploads:/data:ro -v "$PWD/backup":/backup alpine tar czf /backup/uploads.tgz -C /data .
sudo cp plane.env compose.override.yaml backup/
Der Parameter -h /var/run/postgresql ist nötig, weil im Container PGHOST=plane-db gesetzt ist. Ohne ihn verbindet sich pg_dump per TCP und bricht im Test mit fe_sendauth: no password supplied ab, die Sicherungsdatei bleibt leer. Mit dem Socket-Pfad ergab eine frische Instanz einen Dump von 627 KB, der sich fehlerfrei in eine neue Datenbank einspielen ließ und den Administrator enthielt.
Für ein Update laden Sie die Release-Dateien der neuen Version, übernehmen Ihre Werte in die neue plane.env und starten neu. Der migrator passt das Schema an. Ein Zurückstellen des Tags nach der Migration ist kein Rollback, dafür brauchen Sie das Backup von vorher.
cd /opt/plane-app
R=https://github.com/makeplane/plane/releases/download/NEUE_VERSION
sudo curl -fsSLo docker-compose.yaml $R/docker-compose.yml
sudo curl -fsSLo variables.env.neu $R/variables.env
diff plane.env variables.env.neu
sudo sed -i "s|^APP_RELEASE=.*|APP_RELEASE=NEUE_VERSION|" plane.env
docker compose --env-file plane.env pull
docker compose --env-file plane.env up -d
Verifizieren: ls -lh backup/ zeigt Dump, Upload-Archiv und Konfiguration. Nach einem Update meldet docker compose --env-file plane.env logs migrator keine Fehler, und docker compose --env-file plane.env images api zeigt den neuen Tag.
Typische Fehler
| Symptom | Ursache | Lösung |
|---|---|---|
| Migrator und API können sich nicht an der Datenbank anmelden | POSTGRES_PASSWORD geändert, DATABASE_URL leer | DATABASE_URL mit neuem Passwort setzen (Schritt 2), bei bereits initialisierter Datenbank das Passwort per ALTER ROLE angleichen |
port is already allocated für 80 oder 443 | Proxy-Ports kollidieren mit vorhandenem Webserver | LISTEN_HTTP_PORT/LISTEN_HTTPS_PORT ändern und Override aus Schritt 3 anlegen |
| Override wirkt nicht, Ports offen auf 0.0.0.0 | Compose mit -f docker-compose.yaml oder über setup.sh gestartet | Im Projektordner ohne -f starten oder beide Dateien mit -f angeben |
| Migrator beendet sich mit Fehler | Datenbank nicht erreichbar oder falsche Zugangsdaten | docker compose --env-file plane.env logs migrator plane-db prüfen |
| Uploads schlagen fehl | Datei größer als FILE_SIZE_LIMIT oder Proxy-Limit | Beide Werte erhöhen, Stack neu starten |
Links zeigen auf localhost | WEB_URL und CORS_ALLOWED_ORIGINS nicht angepasst | Öffentliche HTTPS-Adresse eintragen, up -d |
Häufige Fragen
Community oder Commercial Edition?
Die Commercial Edition startet laut Herstellerdoku im Free-Plan mit 12 Plätzen je Workspace und wird über prime-cli installiert. Die Community Edition hat keine Platzgrenze, bietet aber weniger Funktionen. Diese Anleitung behandelt nur die Community Edition.
Warum nicht latest oder stable?
stable wandert mit jedem Release weiter. Ein ungeplantes pull würde dann eine neue Version mit Migration einspielen. Ein fester Tag macht Updates zu einer bewussten Entscheidung.
Brauche ich RabbitMQ und MinIO?
Ja, im Standardaufbau. RabbitMQ verteilt Hintergrundaufgaben an die Worker, MinIO speichert Uploads. Die Compose-Datei erlaubt externe Dienste über AMQP_URL und die AWS_*-Variablen, die Herstellerdoku empfiehlt für den Produktivbetrieb externe Datenbank und externen Speicher.
Fazit
Plane Community Edition bringt ein vollständiges Projektmanagement mit, verlangt aber einen Stack aus 13 Containern und entsprechend Arbeitsspeicher. Die häufigste Stolperfalle liegt in der Konfiguration: Wer Passwörter ändert, muss auch die Verbindungs-URLs anpassen. Mit festem Release-Tag, lokal gebundenem Proxy, sofort angelegtem Instanz-Administrator und einem Datenbank-Dump vor jedem Update bleibt der Betrieb berechenbar. Für einfache Kanban-Boards ohne Zyklen und Module reicht ein leichteres Werkzeug wie PLANKA.
Weiterführende Anleitungen und Quellen
- Docker Compose: Multi-Container-Stacks aufbauen
- PostgreSQL mit pg_dump und pg_restore sichern
- Plane-Dokumentation: Docker Compose (Community Edition)
- Plane-Dokumentation: Externer Reverse Proxy
- Plane-Dokumentation: Instance admin und God Mode
- Plane Release v1.4.2 mit docker-compose.yml und variables.env
- Docker-Doku: Compose-Dateien zusammenführen,
!override


