byteNative
Entwicklung

Ohne Kubernetes deployen: Compose, Nginx und ein Skript

Kubernetes ist großartig, wenn du die Probleme hast, für die es gebaut wurde. Die meisten Projekte haben sie nicht und zahlen trotzdem den Preis. Hier steht, was stattdessen trägt.

9 Min. LesezeitStand Februar 2026

Wann Kubernetes die falsche Antwort ist

Kubernetes löst Probleme, die entstehen, wenn viele Teams viele Dienste unabhängig voneinander ausrollen. Wenn du zwei Container und eine Datenbank betreibst, hast du keins dieser Probleme, aber alle Kosten: Manifeste, Ingress-Controller, Zertifikatsverwaltung, Cluster-Updates und eine Fehlersuche, die drei Abstraktionsebenen tief geht.

Für ein Team unserer Größe ist das ein schlechter Tausch. Wir betreiben Anwendungen mit Docker Compose, Nginx davor und einem Deploy-Skript, und das trägt bis in Bereiche, die viele überraschen. Die Streaming-Plattform, über die wir beim Skalieren von Socket.IO schreiben, läuft genau so.

Das ist keine Ideologie. Es ist die Beobachtung, dass die Zeit, die in Cluster-Pflege fließt, bei kleinen Teams direkt von der Zeit abgeht, die in das Produkt fließt.

Der Stack in einer Datei

Eine Compose-Datei beschreibt den ganzen Betrieb: Anwendung, Datenhaltung, Proxy. Drei Dinge machen sie produktionstauglich, die in Beispielen aus dem Netz oft fehlen. Ein Healthcheck, damit Abhängigkeiten in der richtigen Reihenfolge starten. Eine Neustart-Regel, damit ein abgestürzter Container von allein wiederkommt. Und eine Begrenzung der Logs, sonst füllt dir irgendwann die Festplatte den Server.

yaml
services:
  app:
    image: registry.example.com/app:$APP_VERSION
    restart: unless-stopped
    env_file: .env.production
    depends_on:
      redis:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "node", "healthcheck.js"]
      interval: 10s
      timeout: 3s
      retries: 3
      start_period: 20s
    logging:
      driver: json-file
      options: { max-size: "10m", max-file: "3" }

  redis:
    image: redis:7-alpine
    restart: unless-stopped
    command: redis-server --appendonly yes
    volumes: [redis-data:/data]
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s

  nginx:
    image: nginx:alpine
    restart: unless-stopped
    ports: ["80:80", "443:443"]
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
      - ./certs:/etc/nginx/certs:ro
    depends_on: [app]

volumes:
  redis-data:
Healthcheck, restart-Regel und Log-Begrenzung sind die drei Zeilen, die aus einem Entwicklungs-Setup ein betreibbares macht.

Das Deployment als Skript

Ein Deployment ist am Ende: neues Image ziehen, Container tauschen, prüfen ob es lebt, und wenn nicht, zurück auf die alte Version. Das sind zwanzig Zeilen Bash, die in der CI laufen und über SSH auf den Server greifen.

Der Teil, der oft fehlt, ist die Prüfung danach. Ohne sie erfährst du von einem kaputten Deployment durch einen Kunden. Mit ihr rollt das Skript selbst zurück, bevor jemand etwas merkt.

bash
#!/usr/bin/env bash
set -euo pipefail

VERSION="$1"
REMOTE="deploy@app.example.com"
DIR="/srv/app"

ssh "$REMOTE" bash -s <<EOF
set -euo pipefail
cd "$DIR"

# aktuelle version merken, damit ein rollback moeglich bleibt
PREVIOUS=\$(grep APP_VERSION .env.production | cut -d= -f2)
echo "APP_VERSION=$VERSION" > .env.production.new
grep -v APP_VERSION .env.production >> .env.production.new
mv .env.production.new .env.production

docker compose pull app
docker compose up -d --no-deps app

# 60 sekunden auf gesundheit warten, sonst zurueck
for i in \$(seq 1 12); do
  if curl -fsS localhost:3000/health > /dev/null; then
    echo "deployment ok"
    exit 0
  fi
  sleep 5
done

echo "health check fehlgeschlagen, rollback auf \$PREVIOUS"
sed -i "s/APP_VERSION=.*/APP_VERSION=\$PREVIOUS/" .env.production
docker compose up -d --no-deps app
exit 1
EOF
Der Healthcheck nach dem Tausch ist der Unterschied zwischen einem Deployment und einem Hoffnungsakt.

Ohne Ausfall ausrollen, ohne Orchestrator

Der Trick ist derselbe wie überall: kurz zwei Versionen parallel laufen lassen und den Proxy umschalten. Du startest die neue Version unter einem zweiten Dienstnamen, wartest auf ihren Healthcheck, tauschst im Nginx den Upstream und lädst die Konfiguration mit einem Reload nach. Bestehende Verbindungen laufen dabei aus, statt getrennt zu werden.

Für Anwendungen mit dauerhaften Verbindungen ist genau das wichtig. Ein `nginx -s reload` beendet keine offene WebSocket-Verbindung, ein Neustart des Containers schon. Wer das verwechselt, wirft bei jedem Deployment alle Nutzer aus der Sitzung.

Ehrlicherweise: Wenn dir eine Ausfallzeit von zwei Sekunden nichts ausmacht, lass das weg. Die meisten internen Werkzeuge und viele Websites brauchen es schlicht nicht, und jede Zeile, die du nicht baust, kann auch nicht kaputtgehen.

Wann du doch wechseln solltest

Es gibt klare Punkte, an denen der Aufwand kippt. Wenn mehrere Teams unabhängig voneinander ausrollen wollen, wird die Koordination über eine gemeinsame Compose-Datei zur Bremse. Wenn deine Last so stark schwankt, dass du automatisch skalieren musst, statt Kapazität vorzuhalten. Oder wenn ihr aus Compliance-Gründen Dinge wie feingranulare Rechte und Audit-Trails auf Infrastrukturebene braucht.

Was kein Grund ist: dass es alle machen, oder dass es im Lebenslauf gut aussieht. Beides sind reale Motive, und beide kosten dein Projekt Geld.

Häufige Fragen

Ist Docker Compose überhaupt für Produktion gedacht?

Ja. Der Ruf als reines Entwicklungswerkzeug stammt aus einer Zeit, in der es das war. Mit Healthchecks, Neustart-Regeln und einem sauberen Deploy-Weg betreibt man damit ernsthafte Anwendungen, solange alles auf einer Maschine läuft.

Was ist mit Docker Swarm?

Swarm sitzt genau dazwischen und wird kaum noch weiterentwickelt. Wenn Compose auf einer Maschine nicht mehr reicht, würden wir eher direkt zu einem verwalteten Dienst greifen als zu Swarm.

Wie kommen die Secrets auf den Server?

Nicht ins Repository. Wir legen sie als Datei auf dem Server ab, mit strengen Dateirechten, und ziehen sie über env_file. Was aus der CI kommt, liegt dort in den verschlüsselten Variablen. Ein Passwort-Manager bleibt die Quelle der Wahrheit.

Ab wie vielen Nutzern reicht eine Maschine nicht mehr?

Die Nutzerzahl ist selten die Grenze. Meistens ist es entweder eine einzelne teure Operation oder der Wunsch, keine Ausfallzeit mehr zu haben. Beides merkt man rechtzeitig, wenn man misst, statt zu schätzen.

Dieser Beitrag gibt den Stand von Februar 2026 wieder und ersetzt keine Rechtsberatung. Für verbindliche Auskünfte zu deinem konkreten Fall wende dich an eine Anwältin oder einen Anwalt.

Unsicher, wo deine Seite steht?

Wir schauen einmal drüber und sagen dir ehrlich, was zu tun ist. Das Erstgespräch kostet dich nichts.