Praxisleitfaden mit Docker oder Podman Compose, PostgreSQL, Memcached, Hocuspocus, Nginx Reverse Proxy und TLS
1. Einleitung
OpenProject ist eine leistungsfähige Open-Source-Plattform für Projektmanagement, Aufgabenverwaltung, Roadmaps, agile Boards, Gantt-Diagramme, Team-Kollaboration und Dokumentation. Die Software eignet sich für Organisationen, die Projekte transparent planen und steuern möchten, ohne vollständig von proprietären Plattformen abhängig zu sein.
Gerade im Kontext digitaler Souveränität spielt OpenProject eine wichtige Rolle. Viele Organisationen suchen nach Alternativen zu klassischen Atlassian-Umgebungen. In Kombination mit XWiki kann OpenProject eine offene Plattformlandschaft bilden: OpenProject übernimmt Projektmanagement, Tickets, Roadmaps und Aufgabensteuerung, während XWiki Wissensmanagement und technische Dokumentation abdeckt.
Dieser Beitrag beschreibt eine manuelle Installation von OpenProject als Container-Deployment. Der Fokus liegt bewusst nicht auf einer Paketinstallation über DEB oder RPM. OpenProject empfiehlt inzwischen die Docker-basierte Installation, und für skalierbare Umgebungen steht mit Helm auch ein Kubernetes-Weg zur Verfügung. Paketbasierte Installationen bleiben zwar für bestimmte Distributionen verfügbar, sind aber nicht mehr der zukunftssichere Standard für neue Linux-Versionen.
Als Beispiel verwenden wir:
- Domain:
projects.example.org - Runtime-User:
openproject - Public URL:
https://projects.example.org - OpenProject-Image:
openproject/openproject:17-slim - PostgreSQL: Version
17 - interner OpenProject-Port:
127.0.0.1:6000 - interner Hocuspocus-Port:
127.0.0.1:6001 - erster Administrator:
Anna Berger - Projektleiter:
Max Schneider
Die spätere Automatisierung dieser Schritte mit Ansible wird in einem folgenden Beitrag behandelt.
2. Ziel des Beitrags
Ziel ist eine produktionsnahe OpenProject-Installation, die sauber in mehrere Komponenten getrennt ist. OpenProject läuft nicht als einzelner All-in-one-Testcontainer, sondern als Compose-Stack mit getrennten Diensten.
Die Umgebung besteht aus:
- OpenProject Web
- OpenProject Worker
- OpenProject Cron
- OpenProject Seeder
- PostgreSQL
- Memcached
- optional Hocuspocus für kollaborative Bearbeitung
- Nginx als Reverse Proxy
- TLS-Zertifikat für den öffentlichen Zugriff
Diese Trennung macht die Installation transparenter und langfristig besser wartbar. Web-Requests, Hintergrundjobs, Cron-Aufgaben, Datenbank, Cache und kollaborative Echtzeit-Komponenten laufen als getrennte Dienste und können getrennt beobachtet, aktualisiert und debuggt werden.
3. Warum Container statt klassischer Paketinstallation?
OpenProject unterstützt weiterhin paketbasierte Installationen für ausgewählte Distributionen. Gleichzeitig wird in der offiziellen Dokumentation klar, dass Container-basierte Installationen empfohlen werden. Außerdem weist OpenProject darauf hin, dass einige zukünftige Funktionen nur noch mit Container-basierten Installationen ausgeliefert werden sollen und dass für neuere Linux-Versionen keine neuen Paket-Repositories geplant sind.
Für neue produktive Setups ist ein Container-Deployment deshalb der sinnvollere Weg. Es reduziert Abhängigkeiten zum Host-Betriebssystem, vereinfacht Updates und passt besser zu modernen Betriebsmodellen mit Docker Compose, Podman Compose oder Kubernetes.
Die klassische Paketinstallation kann weiterhin sinnvoll sein, wenn:
- eine offiziell unterstützte Distribution eingesetzt wird
- ein dedizierter Server nur für OpenProject bereitsteht
- die Paket-Wizard-Konfiguration bewusst genutzt werden soll
- bestehende Betriebsprozesse DEB/RPM-Pakete bevorzugen
Für neue Umgebungen empfiehlt sich jedoch ein Container-Setup. In diesem Artikel wird daher ein manuelles Compose-Deployment gezeigt.
4. Zielarchitektur
Die Zielarchitektur trennt öffentlichen Zugriff, Applikationsdienste und persistente Daten.
Client / Browser|| HTTPSvNginx Reverse Proxy|| HTTP lokal :6000vOpenProject Web|+--> OpenProject Worker+--> OpenProject Cron+--> Memcached+--> PostgreSQL|| WebSocket /hocuspocusvHocuspocus
Nginx ist die einzige öffentlich erreichbare Komponente. Der OpenProject-Webcontainer lauscht nur lokal auf 127.0.0.1:6000. Hocuspocus, falls aktiviert, lauscht nur lokal auf 127.0.0.1:6001. PostgreSQL und Memcached bleiben vollständig im internen Compose-Netz.
Wichtige Prinzipien:
- kein direkter öffentlicher Zugriff auf Container-Ports
- TLS-Terminierung über Nginx
- persistente PostgreSQL-Daten außerhalb der Container
- persistente OpenProject-Assets außerhalb der Container
- Secrets in einer geschützten
.env-Datei - feste Image-Tags statt unkontrollierter Floating-Tags
5. Voraussetzungen
Für das Deployment wird ein Linux-Server mit Root- oder sudo-Rechten benötigt. Der DNS-Eintrag für projects.example.org sollte bereits auf den Server zeigen. Port 80 und 443 müssen erreichbar sein, damit HTTP-Weiterleitung und TLS funktionieren.
Benötigt werden:
- Docker Compose oder Podman Compose
- Nginx
- Certbot
- OpenSSL
- ausreichend Speicherplatz für Datenbank, Assets und Backups
- funktionierende DNS-Auflösung
- ein SMTP-Relay für produktive E-Mails
In diesem Beitrag wird Podman Compose gezeigt. Wer Docker nutzt, kann die Compose-Datei weitgehend identisch verwenden und die Befehle entsprechend auf docker compose anpassen.
6. Host-Pakete installieren
Zuerst werden die Host-Pakete installiert. Je nach Distribution unterscheiden sich die Paketnamen leicht.
Debian-basierte Systeme
apt updateapt upgrade -yapt install -y podman podman-compose nginx certbot python3-certbot-nginx openssl uidmap curl
Red Hat-basierte Systeme
dnf update -ydnf install -y podman podman-compose nginx certbot python3-certbot-nginx openssl policycoreutils-python-utils curl
SUSE-basierte Systeme
zypper refreshzypper update -yzypper install -y podman podman-compose nginx certbot python3-certbot-nginx openssl curl
Danach sollte geprüft werden, ob Podman und Compose verfügbar sind.
podman --versionpodman-compose --versionnginx -v
7. Benutzer und Verzeichnisstruktur vorbereiten
OpenProject erhält einen eigenen technischen Benutzer. Die Anwendung selbst läuft in Containern, aber Compose-Dateien, Assets, Backups und Datenbankverzeichnisse sollen auf dem Host sauber strukturiert abgelegt werden.
groupadd --system openprojectuseradd \--system \--home-dir /srv/openproject \--shell /sbin/nologin \--gid openproject \openproject
Die Verzeichnisse werden getrennt nach Compose-Projekt, Assets, PostgreSQL-Daten und Backups angelegt.
install -d -o openproject -g openproject -m 0755 /srv/openprojectinstall -d -o openproject -g openproject -m 0755 /srv/openproject/httpdocsinstall -d -o openproject -g openproject -m 0750 /srv/openproject/logsinstall -d -o openproject -g openproject -m 0750 /srv/openproject/scriptsinstall -d -o openproject -g openproject -m 0750 /srv/openproject/tmpinstall -d -o openproject -g openproject -m 0755 /var/db/openprojectinstall -d -o openproject -g openproject -m 0755 /var/db/openproject/assetsinstall -d -o openproject -g openproject -m 0750 /var/db/openproject/backupsinstall -d -o openproject -g openproject -m 0755 /var/lib/postgresql/openproject
Auf SELinux-Systemen muss zusätzlich sichergestellt werden, dass die Container auf die persistenten Verzeichnisse zugreifen dürfen.
semanage fcontext -a -t container_file_t '/var/db/openproject(/.*)?'semanage fcontext -a -t container_file_t '/var/lib/postgresql/openproject(/.*)?'restorecon -RFv /var/db/openproject /var/lib/postgresql/openproject
8. Secrets erzeugen
Für OpenProject werden mehrere Secrets benötigt: SECRET_KEY_BASE, das PostgreSQL-Passwort und bei aktivierter kollaborativer Bearbeitung ein Hocuspocus-Secret.
install -d -o root -g openproject -m 0750 /srv/openproject/httpdocsSECRET_KEY_BASE="$(openssl rand -hex 64)"POSTGRES_PASSWORD="$(openssl rand -hex 24)"COLLABORATIVE_SERVER_SECRET="$(openssl rand -hex 32)"ADMIN_BOOTSTRAP_PASSWORD="$(openssl rand -hex 18)"
Diese Werte werden später in einer .env-Datei abgelegt. Die Datei muss geschützt werden, da sie Anwendungsschlüssel und Datenbankpasswörter enthält.
9. Environment-Datei erstellen
Die .env-Datei enthält Image-Tags, Datenbankparameter, Hostnamen, Cache-Konfiguration, Hocuspocus-Einstellungen und den initialen Admin-Benutzer.
cat > /srv/openproject/httpdocs/.env <<EOFTAG=17-slimHOCUSPOCUS_TAG=17.4.0POSTGRES_VERSION=17POSTGRES_DB=openprojectPOSTGRES_USER=openprojectPOSTGRES_PASSWORD=${POSTGRES_PASSWORD}SECRET_KEY_BASE=${SECRET_KEY_BASE}DATABASE_URL=postgres://openproject:${POSTGRES_PASSWORD}@db/openproject?pool=20&encoding=unicode&reconnect=trueOPENPROJECT_HOST__NAME=projects.example.orgOPENPROJECT_HTTPS=trueOPENPROJECT_HSTS=trueOPENPROJECT_DEFAULT__LANGUAGE=deOPENPROJECT_RAILS__CACHE__STORE=memcacheOPENPROJECT_CACHE__MEMCACHE__SERVER=cache:11211OPENPROJECT_SEED__ADMIN__USER__NAME=Anna BergerOPENPROJECT_SEED__ADMIN__USER__MAIL=anna.berger@example.orgOPENPROJECT_SEED__ADMIN__USER__PASSWORD=${ADMIN_BOOTSTRAP_PASSWORD}OPENPROJECT_SEED__ADMIN__USER__PASSWORD__RESET=trueOPENPROJECT_SEED__ADMIN__USER__LOCKED=falseCOLLABORATIVE_SERVER_SECRET=${COLLABORATIVE_SERVER_SECRET}COLLABORATIVE_SERVER_URL=wss://projects.example.org/hocuspocusOPENPROJECT_COLLABORATIVE__EDITING__HOCUSPOCUS__URL=wss://projects.example.org/hocuspocusOPENPROJECT_COLLABORATIVE__EDITING__HOCUSPOCUS__SECRET=${COLLABORATIVE_SERVER_SECRET}OPENPROJECT_INTERNAL_URL=http://web:8080EOFchown root:openproject /srv/openproject/httpdocs/.envchmod 0600 /srv/openproject/httpdocs/.env
Der Login-Name des initialen OpenProject-Administrators bleibt in vielen Setups admin. Name, E-Mail und Startpasswort werden über die Seed-Variablen gesetzt. Das Passwort sollte beim ersten Login geändert werden.
10. Compose-Datei erstellen
Die Compose-Datei trennt die OpenProject-Dienste in Web, Worker, Cron und Seeder. Dazu kommen PostgreSQL, Memcached und optional Hocuspocus.
name: openprojectnetworks:backend:x-op-restart-policy: &restart_policyrestart: unless-stoppedx-op-image: &imageimage: "docker.io/openproject/openproject:${TAG:-17-slim}"x-op-app: &app<<: [*image, *restart_policy]env_file:- .envvolumes:- "/var/db/openproject/assets:/var/openproject/assets:Z,U"networks:- backendservices:db:image: "docker.io/library/postgres:${POSTGRES_VERSION:-17}"<<: *restart_policystop_grace_period: "3s"volumes:- "/var/lib/postgresql/openproject:/var/lib/postgresql/data:Z,U"environment:POSTGRES_DB: "${POSTGRES_DB:-openproject}"POSTGRES_USER: "${POSTGRES_USER:-openproject}"POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}"networks:- backendcache:image: "docker.io/library/memcached:1.6"<<: *restart_policynetworks:- backendweb:<<: *appcommand: "./docker/prod/web"hostname: "${OPENPROJECT_HOST__NAME:-localhost:8080}"ports:- "127.0.0.1:6000:8080"depends_on:- db- cache- seederhealthcheck:test: ["CMD", "curl", "-f", "http://localhost:8080/health_checks/default"]interval: 10stimeout: 3sretries: 3start_period: 30sworker:<<: *appcommand: "./docker/prod/worker"depends_on:- db- cache- seedercron:<<: *appcommand: "./docker/prod/cron"depends_on:- db- cache- seederseeder:<<: *appcommand: "./docker/prod/seeder"restart: on-failurehocuspocus:image: "docker.io/openproject/hocuspocus:${HOCUSPOCUS_TAG:-17.4.0}"<<: *restart_policyports:- "127.0.0.1:6001:1234"environment:SECRET: "${COLLABORATIVE_SERVER_SECRET}"OPENPROJECT_URL: "${OPENPROJECT_INTERNAL_URL:-http://web:8080}"OPENPROJECT_HTTPS: "${OPENPROJECT_HTTPS:-true}"networks:- backend
Die Datei wird unter /srv/openproject/httpdocs/docker-compose.yaml gespeichert.
editor /srv/openproject/httpdocs/docker-compose.yamlchown root:openproject /srv/openproject/httpdocs/docker-compose.yamlchmod 0644 /srv/openproject/httpdocs/docker-compose.yaml
11. OpenProject manuell starten
Der Stack kann zunächst manuell gestartet werden, bevor ein systemd-Service eingerichtet wird.
cd /srv/openproject/httpdocspodman-compose --file docker-compose.yaml --env-file .env up -d
Der Status wird mit folgenden Befehlen geprüft:
podman pspodman-compose --file docker-compose.yaml --env-file .env logs -f webpodman-compose --file docker-compose.yaml --env-file .env logs -f seeder
Beim ersten Start initialisiert der Seeder die Datenbank. Je nach Systemleistung kann dieser Schritt einige Minuten dauern.
12. systemd-Service einrichten
Für den produktiven Betrieb sollte der Compose-Stack über systemd gestartet werden.
[Unit]Description=OpenProject Podman Compose stackWants=network-online.targetAfter=network-online.target[Service]Type=oneshotRemainAfterExit=yesWorkingDirectory=/srv/openproject/httpdocsTimeoutStartSec=900TimeoutStopSec=240ExecStart=/usr/bin/podman-compose --file /srv/openproject/httpdocs/docker-compose.yaml --env-file /srv/openproject/httpdocs/.env up -d --remove-orphansExecStop=/usr/bin/podman-compose --file /srv/openproject/httpdocs/docker-compose.yaml --env-file /srv/openproject/httpdocs/.env downExecReload=/usr/bin/podman-compose --file /srv/openproject/httpdocs/docker-compose.yaml --env-file /srv/openproject/httpdocs/.env up -d --remove-orphans[Install]WantedBy=multi-user.target
editor /etc/systemd/system/openproject.servicesystemctl daemon-reloadsystemctl enable --now openprojectsystemctl status openproject --no-pager
13. Nginx Reverse Proxy konfigurieren
OpenProject selbst bleibt nur lokal erreichbar. Nginx übernimmt HTTPS, Weiterleitung, Proxy-Header, Upload-Limits und optional WebSocket-Proxying für Hocuspocu
upstream backend.projects.example.org {server 127.0.0.1:6000;keepalive 64;}upstream hocuspocus.projects.example.org {server 127.0.0.1:6001;keepalive 16;}server {listen 80;listen [::]:80;server_name projects.example.org;client_max_body_size 128m;return 301 https://$host$request_uri;}server {listen 443 ssl http2;listen [::]:443 ssl http2;server_name projects.example.org;server_tokens off;client_max_body_size 128m;ssl_certificate /etc/letsencrypt/live/projects.example.org/fullchain.pem;ssl_certificate_key /etc/letsencrypt/live/projects.example.org/privkey.pem;location /hocuspocus {proxy_http_version 1.1;proxy_buffering off;proxy_request_buffering off;proxy_set_header Upgrade $http_upgrade;proxy_set_header Connection "upgrade";proxy_set_header Host $host;proxy_set_header X-Forwarded-Host $host:$server_port;proxy_set_header X-Forwarded-Port $server_port;proxy_set_header X-Forwarded-Proto https;proxy_set_header X-Forwarded-Ssl on;proxy_redirect off;proxy_pass http://hocuspocus.projects.example.org;}location / {proxy_http_version 1.1;proxy_buffering off;proxy_request_buffering off;proxy_connect_timeout 60s;proxy_send_timeout 300s;proxy_read_timeout 300s;proxy_set_header Host $host;proxy_set_header X-Forwarded-Host $host:$server_port;proxy_set_header X-Forwarded-Port $server_port;proxy_set_header X-Forwarded-Proto https;proxy_set_header X-Forwarded-Ssl on;proxy_redirect off;proxy_pass http://backend.projects.example.org;}}
editor /etc/nginx/sites-available/projects.example.orgln -sfn /etc/nginx/sites-available/projects.example.org /etc/nginx/sites-enabled/projects.example.orgnginx -tsystemctl reload nginx
14. TLS aktivieren
Sobald DNS korrekt auf den Server zeigt, kann das Zertifikat ausgestellt werden.
certbot --nginx \-d projects.example.org \--redirect \--agree-tos \--email admin@example.org
Anschließend wird geprüft, ob OpenProject über HTTPS antwortet.
curl -I https://projects.example.org/
15. Erstzugriff und Administrator
Nach dem Start ist OpenProject über die öffentliche URL erreichbar:
https://projects.example.org
Beim ersten Login wird der initiale Administrator verwendet. In vielen Setups ist der Login-Name admin. Das Startpasswort liegt in der .env-Datei als OPENPROJECT_SEED__ADMIN__USER__PASSWORD.
Für den Artikel können folgende Beispieldaten verwendet werden:
- Administratorin:
Anna Berger - E-Mail:
anna.berger@example.org - Projektleiter:
Max Schneider - E-Mail:
max.schneider@example.org - Beispielprojekt:
Intranet-Relaunch
Screenshots:
16. Beispielprojekt anlegen
Nach der technischen Installation sollte ein Beispielprojekt angelegt werden, um Work Packages, Mitglieder und Rollen zu testen.
Beispielstruktur:
Intranet-Relaunch├── Konzept├── Design├── Umsetzung├── Tests└── Go-live
Beispielrollen:
- Anna Berger: Systemadministratorin
- Max Schneider: Projektleiter
- Lena Fischer: Redakteurin
- David Wagner: Entwickler
Damit kann geprüft werden, ob Projekte, Work Packages, Rollen, Benachrichtigungen und Berechtigungen funktionieren.
17. Backups
Für produktive Umgebungen müssen mindestens drei Bereiche gesichert werden:
- PostgreSQL-Datenbank
- OpenProject-Assets
.env-Datei mit Secrets
Ein einfaches Datenbankbackup kann so erstellt werden:
cd /srv/openproject/httpdocspodman-compose --file docker-compose.yaml --env-file .env exec -T db \pg_dump -U openproject openproject > /var/db/openproject/backups/openproject.sql
Assets und Konfiguration werden separat gesichert:
tar -czf /var/db/openproject/backups/openproject-assets.tar.gz /var/db/openproject/assetscp /srv/openproject/httpdocs/.env /var/db/openproject/backups/openproject.envchmod 0600 /var/db/openproject/backups/openproject.env
Backups sollten regelmäßig automatisiert und Wiederherstellungen getestet werden.
18. Updates
Für Updates wird das Image-Tag bewusst angepasst. Produktive Systeme sollten keine unkontrollierten Floating-Tags verwenden.
cd /srv/openproject/httpdocssed -i 's/^TAG=.*/TAG=17-slim/' .envpodman-compose --file docker-compose.yaml --env-file .env pullsystemctl reload openproject
Vor Updates sollten Datenbank und Assets gesichert werden. Außerdem sollte geprüft werden, ob Release Notes migrationsrelevante Hinweise enthalten.
19. Best Practices im produktiven Betrieb
Für stabile OpenProject-Installationen haben sich folgende Grundsätze bewährt:
- feste Image-Tags verwenden
- PostgreSQL-Daten und Assets persistent speichern
.envmit0600schützen- OpenProject nur lokal binden
- öffentlichen Zugriff ausschließlich über Nginx und TLS erlauben
- Backups regelmäßig testen
- Worker, Cron und Web getrennt beobachten
- Hocuspocus nur aktivieren, wenn kollaborative Bearbeitung benötigt wird
- SMTP früh konfigurieren
- Login- und Passwort-Endpunkte rate-limiten
- Updates zuerst in einer Testumgebung prüfen
20. Paketinstallation als Alternative
Die paketbasierte Installation mit DEB oder RPM bleibt für bestimmte Distributionen verfügbar. Sie eignet sich vor allem für dedizierte Server, auf denen OpenProject weitgehend allein betrieben wird und der OpenProject-Installer Webserver, Datenbank und Konfiguration verwalten darf.
Für neue Setups sollte diese Variante jedoch bewusst abgewogen werden. OpenProject stellt klar, dass für neuere Linux-Versionen keine neuen Paket-Repositories geplant sind und dass bestimmte zukünftige Funktionen nur Container-basiert ausgeliefert werden sollen.
Wer langfristig flexibel bleiben möchte, sollte deshalb Docker Compose, Podman Compose oder Kubernetes mit Helm als Zielarchitektur betrachten.
21. Fazit
OpenProject lässt sich als selbst gehostete Projektmanagement- und Kollaborationsplattform sauber betreiben, wenn die Architektur klar getrennt wird. Ein Compose-Stack mit Web, Worker, Cron, Seeder, PostgreSQL, Memcached und optional Hocuspocus bildet eine transparente Grundlage für produktive Umgebungen.
Nginx übernimmt den öffentlichen HTTPS-Zugriff, während die Container nur lokal oder intern erreichbar sind. Persistente Daten liegen außerhalb der Container, Secrets werden geschützt in einer .env-Datei verwaltet und Updates erfolgen über bewusst gesetzte Image-Tags.
Damit entsteht eine langfristig wartbare Open-Source-Alternative für Projektmanagement, Roadmaps, Aufgabensteuerung und Team-Kollaboration.
22. Ausblick
Im nächsten Schritt lässt sich dieses Setup vollständig automatisieren. Eine Ansible-Rolle kann Host-Pakete installieren, Benutzer und Verzeichnisse anlegen, Secrets erzeugen, Compose-Dateien rendern, systemd konfigurieren, Nginx bereitstellen und TLS aktivieren.
Für größere Umgebungen ist außerdem ein Kubernetes-Deployment mit Helm interessant. Damit lassen sich Skalierung, Rollouts, Ressourcenlimits und Plattformintegration deutlich strukturierter abbilden.



