Zum Inhalt springen
LabKompass – zur Startseite
PortainerEinsteiger12 Min. Lesezeit

Portainer Stacks: Docker Compose im Web-Editor deployen

Stacks in Portainer anlegen: Compose-Datei verstehen, per Web-Editor, Upload oder Git deployen, Variablen setzen, Updates einspielen – mit Uptime-Kuma-Beispiel.

Niclas HennrichVeröffentlicht

Vorher hilfreich: Portainer installieren · Container mit Portainer verwalten

Inhalt12 Abschnitte

Getestet mit

  • Portainer CE 2.45.2
  • Docker Engine 29.9.0
  • Docker Compose 5.6.0
  • Debian 13

Technisch verifiziert am

Reality Check

Stand: Oktober 2026

6 Quellen · Was die Stufen bedeuten

Kurz gesagt: Ein Stack ist in Portainer eine Compose-Datei, die als Einheit deployt wird. Du legst ihn unter Stacks → Add stack an, fügst die YAML im Web editor ein, trägst Variablen ein und klickst auf Deploy the stack. Updates laufen über eine geänderte Image-Version und Update the stack; ein echtes Rollback deiner Daten gibt es dabei nicht, das musst du über Backups lösen.

Sobald ein Dienst mehr als einen Container, ein Volume oder ein paar Variablen braucht, wird das Anlegen über einzelne Formulare unübersichtlich. Stacks lösen das: Die komplette Konfiguration steht in einer Datei, die du lesen, kopieren und später in Git ablegen kannst. Dieser Guide richtet sich an alle, die Portainer schon installiert haben und jetzt den ersten Dienst sauber als Stack betreiben wollen. Die Oberflächenschritte folgen der offiziellen Portainer-Doku und der Oberfläche von Portainer CE 2.45.2; Deployment, Variablen und die Ablage der Dateien wurden mit Portainer CE 2.45.2 auf Docker Engine 29.9.0 geprüft.

Voraussetzungen #

  • Portainer CE läuft und du kannst dich anmelden (Portainer installieren).
  • Das lokale Docker-Environment ist verbunden.
  • Grundbegriffe wie Image, Container und Port kennst du (siehe Docker-Grundlagen); wie Portainer einzelne Container verwaltet, zeigt Container in Portainer verwalten.
  • Für den optionalen Test auf der Kommandozeile: Shell-Zugang zum Docker-Host.

Lernziel: Du kannst eine Compose-Datei lesen, sie als Stack deployen, Variablen sinnvoll setzen, den Stack aktualisieren und weißt, wo die Grenzen beim Zurückrollen liegen.

Was ein Stack in Portainer ist #

Hinter einem Stack steckt Docker Compose, das Werkzeug, das mehrere Container aus einer YAML-Datei beschreibt und gemeinsam startet. YAML ist ein Textformat, in dem die Einrückung die Struktur bestimmt. Portainer liefert dafür einen Editor und eine Verwaltungsansicht; die eigentliche Arbeit macht Compose auf dem Docker-Host.

Im Test mit Portainer CE 2.45.2 war das gut nachzuvollziehen:

  • Der Stackname wird zum Compose-Projektnamen. Jeder Container trägt das Label com.docker.compose.project mit diesem Namen, und Volumes sowie das Standardnetzwerk bekommen ihn als Präfix.
  • Portainer legt die Compose-Datei in seinem Datenvolume unter /data/compose/<Stack-ID>/docker-compose.yml ab.
  • Stack-Variablen landen daneben in stack.env, und zwar im Klartext.

Daraus folgt eine praktische Regel: Wähle Stacknamen kurz, klein geschrieben und ohne Leerzeichen, etwa monitoring oder uptime-kuma. Sie tauchen in jedem Container-, Volume- und Netzwerknamen wieder auf.

Eine Compose-Datei lesen #

Das Beispiel für diesen Guide ist Uptime Kuma, ein schlanker Dienst, der Webseiten und Server überwacht. Er braucht genau einen Container, ein Volume für seine Daten und einen Port.

services:
  uptime-kuma:
    image: louislam/uptime-kuma:2.5.6
    restart: unless-stopped
    ports:
      - "3001:3001"
    environment:
      TZ: ${TZ:-Europe/Berlin}
    volumes:
      - kuma-data:/app/data
 
volumes:
  kuma-data:

Von oben nach unten gelesen:

  • services: listet die Dienste. Hier gibt es einen, er heißt uptime-kuma. Unter diesem Namen erreichen ihn auch andere Container im selben Stack.
  • image: nennt das Image mit fester Version. Ein Release-Tag wie 2.5.6 wird vom Herausgeber üblicherweise nicht mehr verändert; latest dagegen kann morgen auf eine andere Version zeigen. Die Version 2.5.6 war zum Testzeitpunkt die neueste stabile Version auf Docker Hub.
  • restart: unless-stopped startet den Container nach einem Absturz oder Neustart des Hosts wieder, außer du hast ihn bewusst gestoppt.
  • ports: veröffentlicht einen Port nach dem Muster Host:Container. Der Dienst lauscht im Container auf 3001 und ist auf dem Host ebenfalls unter 3001 erreichbar – auf allen Adressen des Hosts und an ufw und firewalld vorbei. Soll Uptime Kuma nur im LAN lauschen, schreibst du "192.168.10.20:3001:3001" (Beispieladresse); warum das keine Firewall ersetzt, erklären die Docker-Grundlagen.
  • environment: setzt Umgebungsvariablen im Container. ${TZ:-Europe/Berlin} heißt: Nimm den Wert der Variable TZ, und wenn sie fehlt oder leer ist, Europe/Berlin.
  • volumes: beim Dienst bindet das benannte Volume kuma-data nach /app/data, wo Uptime Kuma seine Datenbank speichert. Der Block volumes: ganz unten legt dieses Volume an.

Ein version:-Feld am Anfang brauchst du nicht. Compose 5.6.0 ignorierte es im Test und gab nur die Warnung aus, das Attribut sei überholt. Eigene Dateien kannst du mit dem Docker-Compose-Generator zusammenstellen und dann hier einfügen.

Den Stack im Web-Editor anlegen #

  1. Öffne in Portainer das Environment, links Stacks, dann Add stack.
  2. Gib einen Namen ein, zum Beispiel uptime-kuma.
  3. Wähle als Methode Web editor und füge die YAML von oben ein.
  4. Optional: Unter Environment variables trägst du TZ mit einem anderen Wert ein. Ohne Eintrag greift der Standard aus der Datei.
  5. Klicke auf Deploy the stack.

Portainer legt Volume, Netzwerk und Container an. Das Image wird beim ersten Mal heruntergeladen, das kann je nach Leitung einen Moment dauern.

Ergebnis prüfen #

Auf dem Docker-Host zeigt docker ps, ob der Container läuft und welchen Gesundheitsstatus er meldet:

docker ps --filter name=uptime-kuma --format '{{.Names}}  {{.Status}}  {{.Ports}}'

Ein Stack namens uptime-kuma ergibt etwa diese Ausgabe:

uptime-kuma-uptime-kuma-1  Up 18 seconds (healthy)  0.0.0.0:3001->3001/tcp, [::]:3001->3001/tcp

Das (healthy) stammt von einem Healthcheck, also einem Befehl, den Docker regelmäßig im Container ausführt, um zu prüfen, ob der Dienst wirklich antwortet und nicht nur der Prozess läuft. Uptime Kuma bringt diesen Check im Image selbst mit; du musst ihn nicht in die YAML schreiben. Im Test stand der Status nach wenigen Sekunden von starting auf healthy.

Danach öffnest du http://docker01.example.lan:3001 im Browser. Version 2 leitet beim ersten Aufruf auf eine Einrichtungsseite um, auf der du die Datenbank wählst; im Test antwortete der Dienst mit einer Weiterleitung auf /setup-database.

Dieselbe Datei auf der Kommandozeile testen #

Die YAML im Web-Editor ist eine normale Compose-Datei. Willst du sie vorher prüfen, speicherst du sie auf dem Host als compose.yaml und lässt Compose die aufgelöste Konfiguration ausgeben:

mkdir -p ~/stack-test && cd ~/stack-test
nano compose.yaml
docker compose -p stacktest config

docker compose config meldet Einrückungsfehler sofort und zeigt, welche Werte nach dem Einsetzen der Variablen tatsächlich verwendet werden. Mit docker compose -p stacktest up -d startest du den Test, mit docker compose -p stacktest down -v räumst du ihn wieder ab; das -v löscht dabei auch das Volume samt Daten, also nur bei Testdaten verwenden. Genau dieses Beispiel lief so mit Docker Engine 29.9.0 und Compose 5.6.0.

Upload und Git als Alternativen #

Neben dem Web-Editor bietet Add stack weitere Wege an:

  • Upload: Du wählst eine Compose-Datei von deinem Rechner. Das Ergebnis entspricht dem Web-Editor, nur ohne Tippen.
  • Repository: Portainer holt die Datei aus einem Git-Repository. Im Abschnitt „Git repository“ wählst du unter Source eine vorhandene Quelle (Select a source) oder legst mit Create new source eine neue an. Dazu kommen Repository reference (Branch oder Tag; ohne Angabe nimmt Portainer den Standard-Branch) und Compose path, der Pfad zur Datei im Repository. Spätere Änderungen machst du dann im Repository, nicht mehr in Portainer. Wie das mit Webhooks und Updates zusammenspielt, steht im Guide zu Git-Stacks (siehe Weiterlesen).
  • Custom template: Deployment aus einer selbst angelegten Vorlage; für den Einstieg nicht nötig.

Für den Einstieg reicht der Web-Editor. Sobald du mehrere Stacks pflegst, lohnt sich Git, weil du dann eine Historie aller Änderungen hast.

Umgebungsvariablen im Stack #

Variablen trennen die Datei von den Werten, die sich je nach Host unterscheiden: Ports, Zeitzonen, Domains, Passwörter. Im Formular kannst du sie einzeln eintragen oder mit Load variables from .env file aus einer Datei übernehmen.

Zwei Wege, wie sie im Container ankommen:

  • Einsetzen in die YAML: Steht ${TZ:-Europe/Berlin} in der Datei, setzt Compose den Wert beim Deployment ein. Im Test mit Portainer CE 2.45.2 wurde ein solcher Platzhalter aus der Stack-Variable befüllt.
  • Als Datei: Mit env_file: stack.env beim Dienst gibt Portainer alle Stack-Variablen als Umgebungsvariablen in den Container. Laut Doku funktioniert das auf Docker Standalone, nicht unter Swarm. Im Test legte Portainer stack.env an, sobald Stack-Variablen gesetzt waren; ob die Datei auch ohne Variablen entsteht, wurde nicht getestet – lege im Zweifel mindestens eine Variable an.
services:
  app:
    image: traefik/whoami:v1.11
    env_file:
      - stack.env

Für Pflichtwerte gibt es eine nützliche Schreibweise: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD fehlt} bricht das Deployment mit dieser Meldung ab, wenn die Variable leer ist. So startet keine Datenbank versehentlich ohne Passwort.

Wichtig für die Sicherheit: Stack-Variablen sind kein Tresor. Portainer speichert sie im Klartext in stack.env, und sie stehen auch im Portainer-Backup – lesbar für jeden, solange du das Backup nicht mit einem Passwort verschlüsselst.

Für Passwörter bietet Compose deshalb Secrets: Der Wert liegt in einer Datei auf dem Host und erscheint im Container als Datei unter /run/secrets/<name> statt als Umgebungsvariable. Viele Images lesen solche Dateien über Variablen mit der Endung _FILE, etwa POSTGRES_PASSWORD_FILE. In Portainer brauchst du dafür einen absoluten Host-Pfad:

services:
  app:
    image: traefik/whoami:v1.11
    secrets:
      - app_passwort
 
secrets:
  app_passwort:
    file: /opt/secrets/app_passwort.txt

Im Test mit Portainer CE 2.45.2 lief ein solcher Stack mit absolutem Pfad, die Datei lag im Container unter /run/secrets/<name>. Mit einem relativen Pfad wie ./app_passwort.txt scheiterte das Deployment dagegen mit bind source path does not exist: /data/compose/<Stack-ID>/app_passwort.txt (gekürzt): Portainer löst relative Pfade gegenüber seinem eigenen Stack-Verzeichnis auf. Die Datei auf dem Host schützt du mit chmod 600. Läuft das Programm im Container nicht als root, muss die Datei dessen Benutzer-ID gehören, sonst meldet es beim Lesen Permission denied.

Volumes und Netzwerke im Stack #

Compose legt pro Stack automatisch an:

  • ein Netzwerk <stackname>_default, in dem sich alle Dienste des Stacks über ihren Dienstnamen erreichen,
  • benannte Volumes mit dem Präfix <stackname>_, im Beispiel also uptime-kuma_kuma-data.

Auf dem Host liegt das Volume unter /var/lib/docker/volumes/. Prüfen kannst du das so:

docker volume inspect -f '{{.Mountpoint}}' uptime-kuma_kuma-data
/var/lib/docker/volumes/uptime-kuma_kuma-data/_data

Benannte Volumes sind für den Anfang die bessere Wahl als relative Pfade wie ./data. Die Compose-Datei liegt bei Portainer nicht in einem Projektordner auf dem Host, sondern im Datenverzeichnis von Portainer; ein relativer Pfad zeigt deshalb nicht dorthin, wo du ihn erwartest – wie beim Secret oben. Für Git-Stacks gibt es dafür die Option Enable relative path volumes, die laut Doku der Business Edition vorbehalten ist. Wenn du Daten direkt auf dem Host bearbeiten willst, nimm einen absoluten Pfad wie /opt/uptime-kuma/data. Die Unterschiede, Rechte und eigene Netzwerke erklärt der Guide zu Netzwerken und Volumes (siehe Weiterlesen).

Einen Stack aktualisieren #

Bei fest gepinnten Versionen ist ein Update eine bewusste Änderung in der Datei:

  1. Stacks → Stack auswählen → Tab Editor.
  2. Die Image-Zeile auf die neue Version anpassen – hättest du mit louislam/uptime-kuma:2.5.5 begonnen, also auf louislam/uptime-kuma:2.5.6.
  3. Update the stack klicken.

Compose erkennt die geänderte Konfiguration, lädt das neue Image, falls es noch nicht auf dem Host liegt, und erzeugt den Container neu. Getestet wurde das mit Compose direkt auf der Kommandozeile, also mit dem Mechanismus, den Portainer für Stacks nutzt: Nach dem Wechsel von 2.5.5 auf 2.5.6 meldete Compose Recreate, der Container hatte eine neue ID, und eine vorher ins Volume geschriebene Datei war unverändert vorhanden. Das Volume überlebt also, der Container selbst wird ersetzt.

Hast du einen Dienst aus der Datei gelöscht, wählst du vor dem Update Prune services, damit auch dessen Container entfernt wird. Für Git-Stacks gibt es stattdessen Pull and redeploy mit der Option Re-pull image and redeploy, die das Image auch bei gleichem Tag neu zieht.

Ein Tipp aus der Praxis: Notiere dir vor dem Update die alte Version. Sie ist dein Rückweg, falls die neue Version nicht startet.

Rollback: was geht und was nicht #

Die Compose-Datei zurückzudrehen ist einfach: alte Image-Version eintragen, Update the stack. Die Portainer-Doku beschreibt im Editor zusätzlich ein Auswahlfeld Version für frühere Fassungen der Stack-Datei. Ob es in deiner Installation angeboten wird, wurde nicht getestet; verlässlicher ist es, die Datei selbst zu versionieren.

Was kein Werkzeug zurückdreht, sind die Daten im Volume. Viele Anwendungen migrieren beim ersten Start einer neuen Version ihre Datenbank. Startet danach wieder die alte Version, findet sie ein Schema vor, das sie nicht kennt. Wie das aussieht, zeigte im Test Portainer selbst: Nach einem Update auf 2.45.2 verweigerte das ältere Image auf demselben Volume den Start mit einem Hinweis, dass das Datenbankschema nicht zur Serverversion passt. Erst das Zurückspielen der Datenbanksicherung, die Portainer vor der Migration selbst angelegt hatte, half, und alle Änderungen seit dem Update waren weg.

Für deine Stacks heißt das: Vor einem Update mit neuer Haupt- oder Nebenversion sicherst du das Volume oder machst einen Datenbank-Dump. Wie du das organisierst, steht in Portainer-Backup.

Typische Probleme #

  • Deployment bricht mit YAML-Fehler ab → Einrückung falsch, oft Tabs statt Leerzeichen → Datei mit docker compose config prüfen; Compose meldet die Zeile, etwa did not find expected key.
  • port is already allocated, not found, pull access denied → reine Docker-Meldungen (Port belegt, Tag oder Image-Name falsch) → Ursachen und Lösungen stehen in den Docker-Grundlagen; im Stack änderst du dann den Host-Port, etwa auf "3002:3001", oder den Tag.
  • bind source path does not exist: /data/compose/… → Ein Bind Mount oder eine Secret-Datei ist relativ angegeben (./…) → absoluten Host-Pfad eintragen.
  • Variable bleibt leer → Name in Portainer und in der YAML unterscheidet sich (Groß-/Kleinschreibung) → mit ${NAME:?…} Pflichtwerte erzwingen.
  • Stack „aktiv“, Dienst trotzdem nicht erreichbar → Im Test galt ein Stack als aktiv, obwohl einzelne Container beendet waren → Containerliste und Logs prüfen, nicht nur den Stack-Status.
  • Container startet immer wieder neu → Anwendung beendet sich mit Fehler → Logs des Containers lesen (siehe auch Portainer-Fehlersuche).

Sicherheit und Betrieb #

  • Feste Versionen statt latest machen Updates planbar und Rollbacks überhaupt erst möglich.
  • Passwörter gehören nicht in die YAML. Stack-Variablen sind der einfache Weg, landen aber im Klartext in stack.env, im Backup und per docker inspect sichtbar im Container; verschlüssele Portainer-Backups mit einem Passwort. Wo das Image _FILE-Variablen unterstützt, sind Secrets mit absolutem Host-Pfad die bessere Wahl.
  • Ports nur veröffentlichen, wenn du von außen zugreifen musst. Dienste, die nur intern miteinander reden, brauchen keine Portfreigabe.
  • Wer in Portainer Stacks anlegen darf, kann beliebige Container auf dem Host starten. Vergib diese Rechte entsprechend sparsam.
  • Den Stack-Webhook für Web-Editor-Stacks (Create a Stack webhook) markiert Portainer CE 2.45.2 als Business Feature, die Doku beschreibt ihn ebenfalls als Funktion der Business Edition. Bei einem Git-Stack funktionierte im Test mit Portainer CE 2.45.2 dagegen der Webhook der GitOps-Updates; automatische Updates planst du in der CE deshalb am besten über Git (siehe Weiterlesen).
  • Ein solcher Webhook ist ohne Anmeldung auslösbar – die URL selbst ist das Geheimnis. Behandle sie wie ein Passwort und lege sie nicht in öffentliche Repositorys oder CI-Logs.

Weiterlesen #

  • Git-Stacks in Portainer zeigt, wie du Compose-Dateien versionierst und Updates per Webhook auslöst.
  • Netzwerke und Volumes in Portainer erklärt, wann ein eigenes Netzwerk und wann ein Bind Mount sinnvoll ist.
  • Portainer aktualisieren behandelt Versionen, Image-Updates und Rollback-Grenzen im Detail.
  • Uptime Kuma und Beszel zeigt, was du mit dem Beispieldienst anschließend sinnvoll überwachst.

Quellen

  1. Offizielle Dokumentation

    Portainer Docs — Add a new stack (externer Link, neues Fenster)

  2. Offizielle Dokumentation

    Portainer Docs — Inspect or edit a stack (externer Link, neues Fenster)

  3. Offizielle Dokumentation

    Portainer Docs — Webhooks (Stacks) (externer Link, neues Fenster)

  4. Offizielle Dokumentation

    Docker Docs — Interpolation (Compose) (externer Link, neues Fenster)

  5. Offizielle Dokumentation

    Docker Docs — Manage secrets securely in Docker Compose (externer Link, neues Fenster)

  6. Offizielle Dokumentation

    Docker Docs — Volumes (externer Link, neues Fenster)

Häufige Fragen

Was ist der Unterschied zwischen einem Stack und einem Container in Portainer?

Ein Container ist ein einzelner laufender Prozess aus einem Image. Ein Stack ist eine Compose-Datei, die einen oder mehrere Container samt Volumes und Netzwerken gemeinsam beschreibt. Portainer startet, aktualisiert und entfernt diese Teile als Einheit, und die Konfiguration bleibt als Datei nachvollziehbar.

Kann ich eine vorhandene docker-compose.yml einfach in Portainer einfügen?

In den meisten Fällen ja: Der Inhalt kommt in den Web-Editor oder wird als Datei hochgeladen. Prüfe vorher relative Pfade wie ./data, denn die Datei liegt in Portainer nicht mehr in deinem Projektordner – trage für Bind Mounts und Secret-Dateien absolute Host-Pfade ein. Variablen aus einer lokalen .env-Datei musst du in Portainer als Stack-Variablen eintragen oder die Datei über die Ladefunktion importieren.

Wo speichert Portainer die Compose-Datei eines Stacks?

Im Datenvolume von Portainer, pro Stack in einem eigenen Unterordner unter /data/compose/ mit der Stack-ID als Namen. Dort liegt auch die Datei stack.env mit den Stack-Variablen im Klartext. Wer Zugriff auf das Portainer-Volume oder ein unverschlüsseltes Portainer-Backup hat, kann diese Werte also lesen.

Gehen meine Daten verloren, wenn ich einen Stack aktualisiere?

Nicht, solange sie in einem Volume oder Bind Mount liegen. Beim Update wird der Container neu erzeugt, das Volume bleibt bestehen und wird wieder eingebunden. Verloren geht nur, was im Container selbst geschrieben wurde. Ein Update kann die Daten aber verändern, etwa durch eine Datenbank-Migration, deshalb gehört vor jedes größere Update ein Backup.

Passt dazu

Werkzeug, Übung und eigene Messung zum Thema.

Teil der Themenwelt Portainer – Docker einfach verwalten · Abschnitt „Container und Docker Compose“

Weiterlesen