// Tutorial · Docker · Self-Hosting

Portainer selbst hosten: Web-UI für dein Docker-Setup

📅 04.09.2026 ⏱ 8 Min. Lesezeit ✍️ Redaktion

Container per CLI starten, Stacks verwalten, Logs durchforsten – irgendwann nervt es. Portainer gibt dir eine übersichtliche Web-Oberfläche für all das: Container, Stacks (Docker Compose), Images, Volumes und Netzwerke lassen sich damit bequem per Klick verwalten. In diesem Tutorial richtest du Portainer CE mit Docker Compose ein – abgesichert über localhost-Bindung, hardened Docker-Socket und Reverse Proxy.

Warum Portainer?

Wer selbst hostet, sammelt schnell dutzende Container an – und verliert zwischen docker ps, docker compose up und Logs langsam den Überblick. Portainer ist eine Web-Oberfläche für Docker: Container starten/stoppen, Stacks per Compose anlegen, Images aktualisieren, Volumes und Netzwerke verwalten – alles per Klick, ohne CLI-Wissen. Die Community Edition (CE) ist kostenlos und deckt für einen einzelnen Host alles ab, was man im Homelab braucht.

Warum eine eigene Instanz? Portainer läuft als kleiner Container neben deinen Diensten und greift über den Docker-Socket auf den Host zu – eine Cloud-Lösung ist nicht nötig, deine Verwaltung bleibt komplett bei dir.

Features auf einen Blick

  • Dashboard: Ressourcen, Container-Status und Events des Docker-Hosts auf einen Blick.
  • Stacks: Docker-Compose-Projekte direkt aus der UI anlegen, bearbeiten und aktualisieren – so entstehen die Setups aus unseren Artikeln per Klick.
  • Container-Ansicht: Logs, Konsole (Web-Terminal), Statistiken, Neu-Start und Neubau ohne SSH.
  • Images, Volumes & Netzwerke: listen, aufräumen (Pruning) und verwalten.
  • Nutzer & Teams: mehrere Accounts mit Rechten – praktisch, wenn mehrere Personen am Server arbeiten.
  • App Templates: vorkonfigurierte Stacks für viele bekannte Dienste als Startpunkt.

Vorbereitung: Socket & Daten

Portainer verwaltet Docker über den Docker-Socket (/var/run/docker.sock). Das ist ein mächtiger Zugriff – wer den Socket hat, hat im Grunde Root auf dem Host. Deshalb gilt: nur an localhost binden und den Zugriff ausschließlich über den eigenen Reverse Proxy erlauben:

mkdir -p ~/portainer/data
cd ~/portainer
  • ./portainer/data/data: hier liegen Datenbank, Einstellungen, Zertifikate und Nutzerkonten – dein komplettes Backup.
  • /var/run/docker.sock wird read-only gemountet – zusätzliche Härtung. Das offizielle Beispiel verzichtet auf :ro; falls bei Stack-Operationen unerwartete Fehler auftauchen, ist das der erste Kandidat zum Testen.
  • /etc/localtime (ro): sorgt dafür, dass Logs und Zeitangaben zur Host-Zeitzone passen.
Socket = Root: Ein kompromittiertes Portainer bedeutet im Zweifel einen kompromittierten Host. Deshalb: starkes Admin-Passwort, UI nur hinter HTTPS und die Instanz nicht öffentlich ins Netz hängen.

Docker Compose – die Datei

So sieht das fertige Setup aus (deine Vorlage, aufgeräumt: leerer networks: {}-Block und überflüssiges network_mode entfernt, Ports in Anführungszeichen):

services:
  portainer:
    container_name: portainer
    image: portainer/portainer-ce:latest
    restart: unless-stopped
    deploy:
      resources:
        limits:
          cpus: "2.0"
          memory: 2G
    ports:
      - "127.0.0.1:50512:9000"        # Web-UI nur lokal
    security_opt:
      - no-new-privileges:true
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./portainer/data:/data
    environment:
      - TRUSTED_ORIGINS=https://pt.meine.domain

Warum die einzelnen Zeilen?

  • 127.0.0.1:50512:9000: Die UI lauscht im Container auf Port 9000 (Portainer CE); gebunden wird nur an localhost, der Zugriff läuft über den Reverse Proxy.
  • no-new-privileges:true: verhindert, dass Prozesse im Container zusätzliche Rechte anfordern (setuid/Setcap).
  • TRUSTED_ORIGINS: erlaubt genau die Origin, über die du die UI aufrufst – verhindert CSRF-Fehler und Login-Schleifen hinter dem Proxy (Details unten).
  • :latest folgt der jeweils neuesten Version – für reproduzierbare Updates lässt sich stattdessen ein konkretes Versions-Tag pinnen (z. B. :2.21.x).

Konfiguration: TRUSTED_ORIGINS & Co.

Der wichtigste Schalter deiner Konfiguration ist TRUSTED_ORIGINS:

VariableWertBedeutung
TRUSTED_ORIGINShttps://pt.meine.domainErlaubte Origin(s) für die UI. Muss exakt zur öffentlichen URL passen (inkl. https://, ohne Pfad). Mehrere Origins kommasepariert angeben.
  • Rufst du die UI zusätzlich über eine zweite Adresse oder IP auf (z. B. https://192.0.2.10 für Tests), dort ebenfalls eintragen – sonst blockiert Portainer den Zugriff.
  • Fehlt die Variable, kann es hinter Reverse Proxys zu CSRF-Meldungen oder Endlos-Redirects beim Login kommen.
  • Weitere Konfiguration erfolgt nach dem ersten Login in der Oberfläche (Einstellungen) – Umgebungsvariablen sind bei Portainer CE bewusst sparsam.
Anonyme Statistik: Portainer fragt beim ersten Login nach der Übermittlung anonymer Nutzungsdaten. Wer maximale Datensparsamkeit will, deaktiviert das unter Einstellungen → Anwendung.

Reverse Proxy & WebSockets

Die UI spricht nur HTTP – also gehört ein TLS-terminierender Proxy davor. Da der Port nur auf localhost lauscht, reicht ein Proxy auf demselben Host. Wichtig: Portainer nutzt für Logs, Konsole und das Web-Terminal WebSockets – die müssen durchgereicht werden:

server {
  listen 443 ssl;
  listen [::]:443 ssl;
  http2 on;
  server_name pt.meine.domain;

  access_log off;
  error_log /var/log/nginx/pt.meine.domain.error.log;
  ssl_certificate /etc/ssl/private/pt.meine.domain_ecc/fullchain.cer;
  ssl_certificate_key /etc/ssl/private/pt.meine.domain_ecc/pt.meine.domain.key;

  ssl_protocols TLSv1.2 TLSv1.3;
  ssl_early_data on;
  ssl_session_cache shared:SSL:50m;
  ssl_session_timeout 1d;

  # Sicherheits-Header
  add_header Strict-Transport-Security "max-age=31536000; includeSubdomains; preload" always;
  add_header X-Content-Type-Options "nosniff" always;
  add_header X-Frame-Options "DENY" always;
  add_header Referrer-Policy "no-referrer" always;

  location / {
    proxy_http_version 1.1;
    # WebSockets für Logs, Konsole & Terminal
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";

    proxy_set_header Host $http_host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Scheme $scheme;

    proxy_read_timeout 3600s;
    proxy_pass http://127.0.0.1:50512;
  }
}
TRUSTED_ORIGINS & Proxy zusammen: Die Variable muss exakt der Adresse entsprechen, unter der der Proxy die UI ausliefert – sonst scheitert der Login mit Origin-Fehlern. Nach einer Änderung den Container neu erstellen (docker compose up -d --force-recreate).

Erster Start: Admin-Konto

cd ~/portainer
docker compose up -d
docker compose logs -f portainer   # bis "Starting Portainer" / ohne Fehler

Danach https://pt.meine.domain öffnen:

  • Admin-Konto anlegen: Benutzername und ein starkes Passwort (mindestens 12 Zeichen) – dieses Konto wird nur einmal erstellt.
  • Anschließend „Get Started“ wählen: Portainer verbindet sich automatisch mit dem lokalen Docker-Socket und zeigt deine laufenden Container.
  • Im Menü „Stacks“ lassen sich jetzt Compose-Projekte aus der UI heraus anlegen – die Configs aus unseren Artikeln kannst du dort direkt einfügen.
  • Wer mit mehreren Personen arbeitet, legt unter Einstellungen → Nutzer/Teams weitere Konten mit eingeschränkten Rechten an.

Backups, Updates & Härtung

Für Updates reicht docker compose pull && docker compose up -d – Portainer legt beim Start eine neue Version der Datenbank an. Trotzdem gilt: vorher ein Backup machen, denn ein Downgrade über mehrere Versionen ist nicht vorgesehen.

Backup: Unter Einstellungen → Backup exportiert Portainer ein Backup der Datenbank – zusätzlich lässt sich der Ordner ./portainer/data sichern. Beides zusammen deckt Nutzer, Einstellungen und Zertifikate ab. Härtung: Admin-Passwort nur im Passwortmanager, Statistik aus, UI ausschließlich hinter HTTPS.

Häufige Probleme (FAQ)

ProblemLösung
Login-Loop / CSRF-Fehler hinter ProxyTRUSTED_ORIGINS exakt auf die öffentliche URL setzen und Container neu erstellen.
Port-Konflikt beim StartExterner Port 50512 ist in unserem Stack bereits durch Open-WebUI (GPU-Variante) belegt – auf einen freien Port ändern.
Stack-Erstellung schlägt fehlBei read-only gemountetem docker.sock: kurz :ro entfernen und testen (offizielles Beispiel mountet ohne).
Admin-Passwort vergessenEs gibt kein Reset über die CLI – letztes Backup unter Einstellungen → Backup wiederherstellen oder das Datenverzeichnis zurücksetzen (Einstellungen gehen verloren).
Terminal/Logs laden nichtWebSocket-Upgrade im Proxy prüfen (Upgrade/Connection-Header, proxy_http_version 1.1).
UI zeigt „Environment nicht verbunden“Socket-Mount prüfen (docker compose exec portainer ls -la /var/run/docker.sock) und ggf. Container neu erstellen.

Fazit

Portainer macht aus der Docker-Verwaltung eine Sache von Klicks statt Kommandozeilen-Suche: Container, Stacks und Volumes auf einen Blick, Logs und Konsolen direkt im Browser. Dank localhost-Bindung, read-only-Socket und no-new-privileges bleibt das Setup dabei erstaunlich schlank – der einzige Preis ist der mächtige Socket-Zugriff, den man mit starkem Passwort und HTTPS davor im Zaum hält.

Die vier Merksätze: ① Socket = Root – nur an localhost binden, nie öffentlich. ② TRUSTED_ORIGINS exakt zur Proxy-URL setzen. ③ WebSockets im Proxy durchreichen (Logs/Terminal). ④ Vor Updates Backup ziehen (Einstellungen → Backup).
📝
....

Wir betreiben unsere komplette Infrastruktur selbst auf Open-Source-Software und schreiben nur über Tools, die wir im Alltag wirklich einsetzen. Fragen zum Artikel? Schreib uns.