TL;DR
- Du baust einen wiederverwendbaren Polycrate-Block, der Nginx und Let's Encrypt (via
community.general.certbot) automatisiert auf einem Linux-Server bereitstellt – inkl. generischernginx.confals Jinja2-Template. - Der Block parametrisiert Domain, Kontakt-E-Mail und Upstream-Port über
block.config.*und kann in jedem Workspace mit anderen Werten eingesetzt werden – ohne Code-Kopie oder Wiki-Bastelei. - Polycrate containerisiert Ansible und die gesamte Toolchain: kein lokales Ansible, kein Python-Chaos, keine individuellen Setups – der gleiche Block läuft auf jeder Developer-Workstation identisch.
- Über eine Registry (
from: cargo.ayedo.cloud/ayedo/infra/nginx-letsencrypt:1.0.0inworkspace.poly) teilst du den Block mit Kolleg:innen: vor dem Push mussnamein derblock.polydie volle OCI-Referenz ohne Tag sein;polycrate blocks pushverwendet genau diesen Namen plusversionals Tag – Sharing statt Screenshots. - ayedo unterstützt Teams dabei, solche standardisierten, teilbaren Automationsblöcke aufzubauen – von der Konzeption der Block-Struktur bis zum Betrieb einer Registry und Integration in bestehende Prozesse.
Das eigentliche Problem: Die vergessene Nginx-Config
Viele Admins kennen das Szenario: Auf web01.acme-corp.com läuft seit Jahren ein Nginx, die nginx.conf wurde "damals schnell" angepasst, Let’s Encrypt irgendwo mit certbot eingerichtet – und niemand weiß mehr genau, wie.
- Warum ist der Upstream-Port 8081?
- Wieso wird hier eine Subdomain umgeleitet?
- Welche certbot-Option war das nochmal?
Dokumentation, wenn überhaupt, liegt in einem Wiki-Eintrag, der längst nicht mehr zur Realität passt. Automatisierung besteht aus einzelnen Shell-Snippets, die nur auf genau diesem Server jemals ausprobiert wurden.
Mit plain Ansible lässt sich das besser machen, aber du kennst die Hürden:
- Lokale Ansible-Installation, Python-Versionen, Collections managen
- Rollen und Playbooks pflegen, verteilen, versionieren
- Kollegen müssen Ansible-CLI, Inventories und Variablenstruktur verstehen
In diesem Beitrag zeige ich dir, wie du aus genau diesem Setup einen wiederverwendbaren Polycrate-Block machst, den deine Kolleg:innen einfach verwenden können – ohne die Implementierung kennen zu müssen.
Von der Server-Config zum wiederverwendbaren Block
Polycrate setzt direkt auf Ansible auf, löst aber zwei typische Probleme:
-
Dependency-Problem:
Ansible läuft in einem Container. Keine lokale Installation, kein Python-Zoo, keine unterschiedlichen Versionsstände im Team. Der Block bringt alles mit, was er braucht. -
Sharable Automation:
Dein Nginx-Setup ist kein fragiler Wiki-Artikel mehr, sondern ein versionierter Block, der in einer Registry liegt. Was du baust, können andere sofort nutzen – mit ihren eigenen Domains und Ports.
Wir konzentrieren uns auf einen konkreten Block:
- Installiert Nginx und certbot auf Ubuntu 22.04
- Holt ein Let’s-Encrypt-Zertifikat für eine Domain
- Deployt eine
nginx.confauf Basis eines Jinja2-Templates - Parametrisiert:
block.config.domainblock.config.emailblock.config.upstream_port
Block-Struktur: Verzeichnis & block.poly
Ausgangspunkt ist ein bestehender Polycrate-Workspace, z.B.:
# workspace.poly
name: acme-corp-automation
organization: acme
Unser Block liegt in ./blocks/nginx-letsencrypt/ und enthält:
block.polynginx-letsencrypt.yml(Ansible-Playbook)nginx.conf.j2(Jinja2-Template)
Verzeichnislayout (Überblick)
So hängen Workspace-Root und Block-Ordner zusammen (Beispiel-Workspace-Name acme-corp-automation):
acme-corp-automation/ # Workspace-Root
├── workspace.poly # Workspace-Konfiguration, u. a. Block-Instanzen
├── inventory.yml # Zentrales Ansible-Inventory (alle Blöcke)
├── blocks/
│ └── nginx-letsencrypt/ # Lokaler Block (Ordnername = Template-Name in from:)
│ ├── block.poly # Block-Definition, Version, config, actions
│ ├── nginx-letsencrypt.yml # Ansible-Playbook (Action deploy)
│ └── nginx.conf.j2 # Jinja2-Template für Nginx
└── … # weitere typische Einträge (z. B. artifacts/, secrets.poly)
Später, wenn du den Block für eine Registry mit vollem OCI-name auslieferst, liegt derselbe Inhalt typischerweise unter einem Pfad, der den Registry-Namen spiegelt, z. B. blocks/registry.acme-corp.com/infra/nginx/ (siehe Abschnitt Registry).
Vollständige block.poly
name: nginx-letsencrypt
version: 1.0.0
kind: generic
config:
domain: ""
email: ""
upstream_port: 8080
actions:
- name: deploy
playbook: nginx-letsencrypt.yml
Wichtige Punkte:
version: 1.0.0– wir versionieren den Block explizit.configdefiniert die Parameter, die später im Workspace gesetzt werden:domain– FQDN wiewww.acme-corp.comemail– Kontakt-E-Mail für Let’s Encryptupstream_port– Port des Backends, auf das Nginx proxyt (z.B. eine App auf 8080)actionsbeschreibt, welche Playbooks verfügbar sind.
Mitpolycrate run nginx-letsencrypt deployführen wir das Playbook aus.
Weitere Details zur Block-Struktur findest du in den offiziellen Block-Konzepte.
nginx.conf als Jinja2-Template
Das Herzstück für Wiederverwendbarkeit ist die Nginx-Konfiguration als Template. Statt fixem Domainnamen und Port verwenden wir die Polycrate-Variablen.
Vollständiges nginx.conf.j2
# nginx.conf für {{ block.config.domain }} – generiert durch Polycrate
server {
listen 80;
server_name {{ block.config.domain }};
# HTTP → HTTPS Redirect
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name {{ block.config.domain }};
ssl_certificate /etc/letsencrypt/live/{{ block.config.domain }}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/{{ block.config.domain }}/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
# Optionales HSTS – bei Bedarf anpassen
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
location / {
proxy_pass http://127.0.0.1:{{ block.config.upstream_port }};
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Was hier passiert:
{{ block.config.domain }}und{{ block.config.upstream_port }}stammen direkt ausblock.configim Block – und werden letztlich im Workspace gesetzt.- Die Zertifikats-Pfade zeigen auf die Standardverzeichnisse von certbot (
/etc/letsencrypt/live/...). - Die Konfiguration ist idempotent aus Ansible heraus deploybar – Änderungen am Template führen zu einem konsistenten Zustand.
Ansible-Playbook für Nginx + Let’s Encrypt
Jetzt verbinden wir alles in einem Playbook. Wichtiger Hinweis in Polycrate: Die Playbooks laufen im Container, aber die Ziel-Hosts sind echte Server aus dem Inventory, nicht localhost.
Inventory im Workspace
Im Workspace-Root liegt die inventory.yml (Polycrate setzt ANSIBLE_INVENTORY automatisch):
# inventory.yml
all:
hosts:
web01.acme-corp.com:
ansible_user: ubuntu
ansible_become: true
Vollständiges Playbook nginx-letsencrypt.yml
---
- name: Nginx mit Let's Encrypt bereitstellen
hosts: all
become: true
vars:
domain_name: "{{ block.config.domain }}"
contact_email: "{{ block.config.email }}"
upstream_port: "{{ block.config.upstream_port }}"
pre_tasks:
- name: Domain muss gesetzt sein
ansible.builtin.assert:
that:
- domain_name | length > 0
- contact_email | length > 0
fail_msg: "block.config.domain und block.config.email müssen im Workspace gesetzt sein."
tasks:
- name: APT-Cache aktualisieren (Ubuntu/Debian)
ansible.builtin.apt:
update_cache: true
cache_valid_time: 3600
- name: Nginx und Certbot installieren
ansible.builtin.apt:
name:
- nginx
- certbot
- python3-certbot
state: present
- name: Standard-Site deaktivieren
ansible.builtin.file:
path: /etc/nginx/sites-enabled/default
state: absent
notify: reload nginx
- name: TLS-Zertifikat von Let's Encrypt holen (Standalone)
community.general.certbot:
email: "{{ contact_email }}"
domains:
- "{{ domain_name }}"
authenticator: standalone
agree_tos: true
installer: none
state: present
register: certbot_result
- name: Certbot-Output anzeigen (Debug)
ansible.builtin.debug:
var: certbot_result
- name: Nginx-Konfiguration deployen
ansible.builtin.template:
src: nginx.conf.j2
dest: "/etc/nginx/sites-available/{{ domain_name }}"
owner: root
group: root
mode: "0644"
notify: reload nginx
- name: Site aktivieren
ansible.builtin.file:
src: "/etc/nginx/sites-available/{{ domain_name }}"
dest: "/etc/nginx/sites-enabled/{{ domain_name }}"
state: link
notify: reload nginx
handlers:
- name: reload nginx
ansible.builtin.service:
name: nginx
state: reloaded
enabled: true
Erläuterungen:
hosts: all– alle Hosts ausinventory.yml. Keineconnection: local, weil wir echte Server verwalten.community.general.certbotstammt aus der Collectioncommunity.general. Polycrate kann diese im Container installieren, ohne dein lokales System zu berühren (siehe Ansible-Integration).- Wir nutzen den
standalone-Authenticator: - Certbot startet kurz einen eigenen Webserver auf Port 80.
- Danach deployen wir die HTTPS-Nginx-Konfiguration.
- Über
varsbinden wir die Polycrate-Variablen (block.config.*) an Ansible-Variablen, was das Lesen im Playbook vereinfacht.
Den Block im Workspace nutzen
Nach dem Build und polycrate blocks push nutzen Workspaces denselben Block wie in den anderen Beiträgen dieser Reihe: vollständige OCI-Referenz unter cargo.ayedo.cloud (nicht nur der kurze Template-Name).
Block im Workspace referenzieren
# workspace.poly
name: acme-corp-automation
organization: acme
blocks:
- name: webserver
from: cargo.ayedo.cloud/ayedo/infra/nginx-letsencrypt:1.0.0
config:
domain: "www.acme-corp.com"
email: "admin@acme-corp.com"
upstream_port: 8080
name: webserverist der Instanzname im Workspace.- Der Block liegt in der Registry unter
cargo.ayedo.cloud. Ein vorherigespolycrate blocks pullist nicht nötig: Fehlt der Block lokal, erkennt Polycrate das beim Aufruf vonpolycrate run …und fragt, ob der Block aus der Registry automatisch installiert werden soll. Danach liegt die entpackte Struktur unterblocks/cargo.ayedo.cloud/ayedo/infra/nginx-letsencrypt/. Infrom:steht die vollständige Referenz inkl. Tag (Block Sharing per OCI statt nur lokalem Ordnerblocks/nginx-letsencrypt/). - Unter
configüberschreiben wir die Default-Werte ausblock.poly.
Aktion ausführen
polycrate run webserver deploy
Polycrate:
- Startet einen Container mit Ansible und allen benötigten Tools.
- Lädt
inventory.ymlaus dem Workspace-Root. - Setzt Variablen wie
block.config.*und führt das Playbook im Container aus. - Verändert den Ziel-Host via SSH – der Container selbst bleibt ephemeral.
Mit plain Ansible sähe das z.B. so aus:
ansible-playbook -i inventory.yml nginx-letsencrypt.yml \
-e "domain_name=www.acme-corp.com contact_email=admin@acme-corp.com upstream_port=8080"
Und du müsstest sicherstellen, dass:
- Ansible lokal installiert ist
- Die richtige Python-Version läuft
- Die Collection
community.generalvorhanden ist
Mit Polycrate sind diese Abhängigkeiten Teil des Blocks bzw. des Workspaces und laufen im Container.
Den Webserver-Block in eine Registry pushen
Jetzt kommt das Sharing-Argument: Statt einen Wiki-Eintrag zu schreiben, veröffentlichst du deinen Block in einer Registry.
Wichtig: Blöcke mit rein lokalen Namen wie nginx-letsencrypt oder nginx lassen sich nicht in eine Registry pushen. polycrate blocks push erwartet, dass name in der block.poly bereits die vollständige OCI-Referenz ohne Versions-Tag ist (Host und Pfad wie bei einem Container-Image-Namen ohne :tag). Die Version steht separat im Feld version: und wird beim Push zum OCI-Tag.
Angenommen, ihr betreibt eine interne Polycrate-/OCI-Registry unter registry.acme-corp.com.
block.poly für die Registry anpassen
Vor dem Push trägst du den Registry-Namen ein und versionierst wie gewohnt:
name: registry.acme-corp.com/infra/nginx
version: 1.0.0
kind: generic
config:
domain: ""
email: ""
upstream_port: 8080
actions:
- name: deploy
playbook: nginx-letsencrypt.yml
Empfehlenswert: Das Block-Verzeichnis spiegelt den Namen, z. B. blocks/registry.acme-corp.com/infra/nginx/ (siehe Best Practices).
Block pushen
polycrate blocks push registry.acme-corp.com/infra/nginx
Es gibt nur dieses eine Argument – den Block-Namen exakt wie in block.poly (name, ohne Tag). Kein zweites Argument für „Ziel-URL“; Host, Pfad und Tag leitet Polycrate aus name und version ab.
Was passiert:
- Polycrate verpackt den Block (inkl.
block.poly, Playbook, Template) als OCI-Artifact. - Der Artifact landet als
registry.acme-corp.com/infra/nginx:1.0.0in eurer Registry (name+versionals Tag). - Für ein Folge-Release erhöhst du
versionin derblock.poly(z. B.1.1.0) und pushst erneut.
Im Workspace: Version pinnen
Wenn andere den Block einbinden, referenzieren sie ihn mit from: inklusive Tag – nach Best Practices pinnt man die Block-Version beim Instanziieren im Workspace explizit, z. B. from: registry.acme-corp.com/infra/nginx:1.0.0 (siehe Registry-Dokumentation).
Kollege nutzt den Block in seinem Workspace
Dein Kollege hat einen anderen Workspace, z.B. für ein internes Portal. Er möchte denselben Nginx-/Let’s-Encrypt-Block nutzen, aber mit anderen Werten.
workspace.poly beim Kollegen
# workspace.poly im Workspace des Kollegen
name: acme-portal-automation
organization: acme
blocks:
- name: portal-web
from: registry.acme-corp.com/infra/nginx:1.0.0
config:
domain: "portal.acme-corp.com"
email: "ops@acme-corp.com"
upstream_port: 9000
Er muss weder:
- Das Playbook lesen
- Die Nginx-Template-Syntax verstehen
- Certbot-Optionen kennen
Er setzt nur die Konfiguration.
Erster polycrate run beim Kollegen
polycrate run portal-web deploy
Beim ersten Aufruf erkennt Polycrate, dass der Block registry.acme-corp.com/infra/nginx:1.0.0 noch nicht lokal installiert ist, und fragt:
Block 'registry.acme-corp.com/infra/nginx:1.0.0' ist nicht installiert.
Möchten Sie den Block jetzt aus der Registry ziehen? [y/N]
Mit y zieht Polycrate den Block, legt ihn im Workspace an und führt die Action aus. Ab diesem Punkt ist der Webserver-Block Teil seines Workspaces – vollständig versioniert und testbar.
Was vorher ein Wiki-Eintrag mit "Bitte so machen" war, ist jetzt ein Block mit:
- Klarem Verhalten (Playbook)
- Interface über
block.config.* - Versionierung und Verteilung über eine Registry
Häufige Fragen
Muss ich Ansible lokal installieren, um diesen Block zu nutzen?
Nein. Polycrate führt Ansible immer im Container aus. Der Container enthält:
- Eine definierte Ansible-Version
- Die benötigten Collections (z.B.
community.generalfürcertbot) - Eine konsistente Toolchain für alle im Team
Du brauchst lokal nur Polycrate und Zugang zur Registry. Das eliminiert das typische Dependency-Problem mit unterschiedlichen Python-/Ansible-Versionen auf Entwickler- und Admin-Maschinen.
Wie gehe ich mit mehreren Domains oder Staging-Umgebungen um?
Das Block-Modell ist exakt dafür gemacht:
- Eine Block-Implementierung (
nginx-letsencrypt). - Mehrere Block-Instanzen im Workspace mit verschiedenen
config-Werten, z.B.:
blocks:
- name: web-prod
from: registry.acme-corp.com/infra/nginx:1.0.0
config:
domain: "www.acme-corp.com"
email: "admin@acme-corp.com"
upstream_port: 8080
- name: web-staging
from: registry.acme-corp.com/infra/nginx:1.0.0
config:
domain: "staging.acme-corp.com"
email: "admin@acme-corp.com"
upstream_port: 8081
Du führst dann z.B. polycrate run web-staging deploy aus. Das Ansible-Playbook bleibt unverändert; nur die Parameter unterscheiden sich.
Wie sicher ist das Teilen eines solchen Blocks?
Der Block selbst enthält nur:
- Struktur (
block.poly) - Playbooks, Templates, Skripte
Vertrauliche Daten wie Passwörter gehören in den Workspace – z.B. in verschlüsselte Secrets. Polycrate unterstützt dich hier mit integrierter Workspace-Verschlüsselung auf Basis von age (siehe Workspace-Verschlüsselung).
Du kannst also:
- Den Block öffentlich oder intern teilen
- Im Workspace sensible Daten verschlüsselt halten
- Trotzdem gemeinsam auf dieselbe Automatisierung aufsetzen
Weitere Fragen? Siehe unsere FAQ
Von der Routine zur Reproduzierbarkeit
Mit diesem Beispiel hast du gesehen, wie du aus einer einmaligen Server-Konfiguration eine wiederverwendbare, versionierte Automatisierung machst:
- Nginx + Let’s Encrypt als Polycrate-Block
- Parametrisiert über
block.config.domain,block.config.emailundblock.config.upstream_port - Vollständig automatisiert per Ansible im Container
- Geteilt über eine Registry, damit Kolleg:innen den Block nutzen können, ohne die Interna zu kennen
Genau hier liegt die Stärke von Polycrate in heterogenen Umgebungen: Du kannst bewährte Setups – ob Webserver, Windows-Policies oder IoT-Deployments – als strukturierte Blöcke kapseln, im Team teilen und über Workspaces sicher betreiben. Die Guardrails des Block-Modells verhindern Playbook-Wildwuchs, die Container-Ausführung löst das Dependency-Problem, und die Registry macht aus Einzelwissen gemeinsame Infrastruktur.
ayedo unterstützt Teams dabei, diese Art von wiederverwendbaren Bausteinen systematisch aufzubauen: vom Design der Block-Schnittstellen über die Einrichtung der Registry bis zur Integration in bestehende CI/CD- und Compliance-Prozesse. Wenn du sehen möchtest, wie ein solcher Webserver-Block in deiner Umgebung konkret aussehen kann, führt der nächste Schritt über unsere Webserver-Block Demo