// Tutorial · Debian · Automation

Server-Deployment mit Ansible: reproduzierbare Debian-Server

📅07.09.2026> ⏱15 Min. Lesezeit ✍️ Redaktion

Mehrere Server, gleiche Konfiguration, kein Handbuch-Gefrickel: Mit Ansible richtest du Debian-Server reproduzierbar ein – agentenlos über SSH und mit idempotenten Playbooks.

Warum Ansible?

Teil 5 richtet Container ein – aber was ist, wenn du mehrere Server gleich oder wiederholt konfigurieren musst? Ansible automatisiert Debian-Server reproduzierbar: gleiche Konfiguration, dokumentiert im Git-Repo, ohne Agenten auf den Zielsystemen.

Konzept: Control Node, Inventory & Playbooks

Ansible arbeitet agentenlos über SSH: Ein Control-Rechner (dein Laptop oder ein Admin-Server) führt Playbooks aus, die Zielserver müssen nur SSH und Python mitbringen. Wichtig ist die Idempotenz: Ein Playbook kann beliebig oft laufen, ohne Zustände doppelt zu setzen.

Vorbereitung

Ansible wird nur auf dem Control-Rechner installiert – die Debian-Zielserver brauchen nichts:

sudo apt install ansible            # Debian-Paket
ansible --version
mkdir -p ~/ansible/{inventories,playbooks}
cd ~/ansible

Voraussetzung: SSH-Key-Zugang zu den Zielservern (ohne Passwort, siehe Debian-Teil 1).

Inventory anlegen

In inventories/hosts tragen wir die Server ein:

[server]
web1 ansible_host=10.0.0.10 ansible_user=lena
vpn1 ansible_host=10.0.0.11 ansible_user=lena

[server:vars]
ansible_python_interpreter=/usr/bin/python3

Erste Befehle (Ad-hoc)

Erreichbarkeit und sudo testen:

ansible -i inventories/hosts -m ping all
ansible -i inventories/hosts -m apt -a "name=htop state=present" -b --become-user=root server

Das -b aktiviert become (sudo) – für Administrationsaufgaben Pflicht.

Das erste Playbook

Datei playbooks/basis.yml – Updates, Basis-Pakete und SSH-Härtung in einem Rutsch:

---
- name: Basis-Server einrichten
  hosts: server
  become: true
  tasks:
    # Kombiniert: Aktualisierung und Installation spart massiv Zeit
    - name: Basis-Pakete installieren und Cache aktualisieren
      ansible.builtin.apt:
        name:
          - htop
          - ufw
          - fail2ban
        state: present
        update_cache: true

    # Blockinfile ist wesentlich robuster und sauberer als eine lineinfile-Schleife
    - name: SSH-Härtung konfigurieren (Drop-in)
      ansible.builtin.blockinfile:
        path: /etc/ssh/sshd_config.d/99-ansible.conf
        create: true
        mode: '0644'
        block: |
          PermitRootLogin no
          PasswordAuthentication no
      notify: SSH sanft neu laden

  handlers:
    # reloaded verhindert das harte Trennen der aktuellen SSH-Sitzung
    - name: SSH sanft neu laden
      ansible.builtin.service:
        name: ssh
        state: reloaded

Ausführen:

ansible-playbook -i inventories/hosts playbooks/basis.yml --check   # Probe
ansible-playbook -i inventories/hosts playbooks/basis.yml
Handlers laufen nur, wenn sich die Konfiguration geändert hat – so startet ssh nur bei echten Änderungen neu.

Secrets mit Ansible Vault

Passwörter gehören nicht ins Playbook. Mit Vault verschlüsseln wir sie:

ansible-vault create secrets.yml
ansible-vault edit secrets.yml
ansible-playbook --ask-vault-pass -i inventories/hosts playbooks/basis.yml
Hinweis: Das Vault-Passwort selbst gehört in einen Passwort-Manager – und nie ins Git-Repo.

Push-Variante mit Semaphore (Web-UI)

Bisher haben wir Playbooks per Terminal gestartet – die Push-Variante. Wer das im Team machen will oder History, Zeitpläne und Zugriffskontrolle braucht, hängt ein Web-Interface davor: Semaphore (Open Source) verwaltet Projekte, Inventories, SSH-Keys und stößt Playbooks per Klick, Zeitplan, Webhook oder API an.

Push vs. Pull: Semaphore bleibt Push – ein zentraler Runner schiebt die Konfiguration zu den Servern. Im Pull-Modell (ansible-pull) holen sich die Zielserver die Playbooks selbst. Semaphore ist der komfortablere Push-Weg für Teams.

Semaphore mit Docker Compose betreiben

Semaphore braucht eine Datenbank (hier MariaDB), um Projekte und Verlauf zu speichern. Passwörter schreiben wir nicht direkt in die docker-compose.yml – so gibt der Artikel keine (Demo-)Config mit echten Werten weiter, und wer sie kopiert, übernimmt keine unsicheren Standard-Passwörter. Stattdessen liest Docker Compose die Werte automatisch aus der Datei .env im selben Ordner wie die docker-compose.yml. Diese erstellst du einmalig mit eigenen, sicheren Passwörtern:

# .env (im selben Ordner wie die docker-compose.yml – NIE ins Git-Repo!)
DB_ROOT_PASS=dein_sicheres_root_passwort
DB_PASS=dein_sicheres_db_passwort

ADMIN_USER=admin
ADMIN_PASS=dein_sicheres_admin_passwort
ADMIN_NAME=Admin
ADMIN_EMAIL=admin@meine.domain

Die docker-compose.yml referenziert die Variablen nur noch per ${…} – so bleibt sie frei von Secrets und darf bedenkenlos im Git-Repo liegen:

services:
  semaphore:
    image: semaphoreui/semaphore:latest
    container_name: semaphore
    restart: unless-stopped
    depends_on:
      - db
    ports:
      - "127.0.0.1:50600:3000"
    environment:
      - SEMAPHORE_DB_USER=semaphore
      - SEMAPHORE_DB_PASS=${DB_PASS}
      - SEMAPHORE_DB_HOST=db
      - SEMAPHORE_DB_PORT=3306
      - SEMAPHORE_DB=semaphore
      - SEMAPHORE_ADMIN=${ADMIN_USER}
      - SEMAPHORE_ADMIN_PASSWORD=${ADMIN_PASS}
      - SEMAPHORE_ADMIN_NAME=${ADMIN_NAME}
      - SEMAPHORE_ADMIN_EMAIL=${ADMIN_EMAIL}
      - SEMAPHORE_TZ=Europe/Berlin
    volumes:
      # WICHTIG: Verhindert, dass SSH-Keys und Repositories bei Updates gelöscht werden
      - ./semaphore-data:/var/lib/semaphore

  db:
    image: mariadb:11
    container_name: semaphore_db
    restart: unless-stopped
    environment:
      - MARIADB_DATABASE=semaphore
      - MARIADB_ROOT_PASSWORD=${DB_ROOT_PASS}
      - MARIADB_USER=semaphore
      - MARIADB_PASSWORD=${DB_PASS}
    volumes:
      - ./semaphore-db:/var/lib/mysql
.env nie einchecken: Docker Compose liest die .env automatisch – genau darum gehört sie ins .gitignore (.env eintragen), während die docker-compose.yml mit den Platzhaltern gefahrlos versioniert werden kann.

Danach erreichbar unter http://127.0.0.1:50600. Beim ersten Login mit den SEMAPHORE_ADMIN_*-Zugangsdaten aus deiner .env anmelden.

Im Projekt dann:

  • Repository: dein Ansible-Ordner als Git-Repo (z. B. Codeberg) – Semaphore klont es beim Lauf.
  • Inventory: die Datei inventories/hosts aus dem Repo wählen.
  • Key: den SSH-Key zum Zielserver im Key-Store hinterlegen.
  • Playbook: playbooks/basis.yml – per Klick, Cron-artigem Zeitplan oder Webhook ausführen.
Container-Reichweite: Semaphore läuft in Docker – der Runner muss die Zielserver über das Netz erreichen und den SSH-Key besitzen. Firewall-Regeln aus Debian-Teil 3 entsprechend öffnen (nur vom Control-Host aus).

FAQ

ProblemLösung
„unreachable“SSH-Key prüfen, Hostname/IP im Inventory korrigieren, ansible -m ping isoliert testen.
sudo-Passwort wird abgefragt-b --ask-become-pass oder Key-basiertes sudo (NOPASSWD) einrichten.
Playbook ist nicht idempotentModule statt command nutzen; --check vor jedem Lauf.
Andere DistributionInventar-Gruppen je Distro anlegen oder ansible_facts["os_family"] verwenden.
Python fehlt auf dem Zielserveransible_python_interpreter=/usr/bin/python3 setzen (Debian 13 hat es).
Rollout über viele Server dauertIn ansible.cfg forks = 10 erhöhen.
Semaphore: Hosts unreachableSSH-Key im Key-Store hinterlegen, Erreichbarkeit aus dem Container testen (docker exec semaphore ssh …).
Semaphore: Playbook nicht gefundenRepository-URL, Branch und Dateipfad im Projekt prüfen; Semaphore klont beim Lauf frisch.
Semaphore: Zeitplan feuert nichtSEMAPHORE_TZ prüfen; Cron läuft in der Container-Zeitzone.

Fazit

Mit Ansible wird aus „ich habe den Server irgendwann eingerichtet“ ein reproduzierbares, dokumentiertes Setup – die perfekte Ergänzung zu Teil 5: erst der Container-Standard, dann die Automatisierung darüber.

Agentenlos über SSH – kein Zusatzdienst nötig
Idempotente Playbooks: immer wieder ausführbar
--check vor jedem Lauf, Vault für Secrets
Playbooks gehören ins Git-Repo
📝
....

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.