- Change registry_accessible to string comparison ('true'/'false') instead of bool
- Fix 'argument of type bool is not iterable' error in when conditions
- Set correct owner/group for .env file (ansible_user instead of root)
- Fixes 'permission denied' error when docker compose reads .env file
Ansible Deployment Configuration
This directory contains Ansible playbooks and configuration for deploying the Custom PHP Framework to production.
Directory Structure
deployment/ansible/
├── ansible.cfg # Ansible configuration
├── inventory/
│ ├── production.yml # Production server inventory
│ └── local.yml # Local testing inventory
├── playbooks/
│ ├── deploy-update.yml # Deploy application updates
│ ├── system-maintenance.yml # Betriebssystem-Updates & Wartung
│ ├── rollback.yml # Rollback deployments
│ ├── setup-infrastructure.yml # Provision core stacks
│ ├── setup-production-secrets.yml # Deploy secrets
│ ├── setup-wireguard.yml # Setup WireGuard VPN server
│ ├── add-wireguard-client.yml # Add WireGuard client
│ ├── sync-code.yml # Git-based code sync
│ └── README-WIREGUARD.md # WireGuard documentation
├── scripts/ # Helper scripts for secrets & credentials
├── roles/ # Reusable roles (e.g. application stack)
├── secrets/
│ ├── .gitignore # Prevent committing secrets
│ └── production.vault.yml.example # Example vault file
└── templates/
├── application.env.j2 # Application stack environment
├── gitea-app.ini.j2 # Gitea configuration
├── minio.env.j2 # MinIO environment
├── monitoring.env.j2 # Monitoring stack environment
├── wireguard-client.conf.j2 # WireGuard client config
└── wireguard-server.conf.j2 # WireGuard server config
## Roles
Stack-spezifische Aufgaben liegen in `roles/` (z. B. `application`, `traefik`, `registry`). Playbooks wie `setup-infrastructure.yml` importieren diese Rollen direkt. Die Application-Rolle kann mit Variablen wie `application_sync_files=false` oder `application_compose_recreate="always"` konfiguriert werden (siehe `playbooks/deploy-update.yml` als Beispiel). Die neue `system`-Rolle hält Betriebssystem-Pakete aktuell und konfiguriert optionale Unattended-Upgrades bevor Docker-Stacks neu gestartet werden.
## Prerequisites
1. **Ansible Installed**:
```bash
pip install ansible
-
SSH Access:
- SSH key configured at
~/.ssh/production - Key added to production server's authorized_keys for
deployuser
- SSH key configured at
-
Ansible Vault Password:
- Create
.vault_passfile insecrets/directory - Add vault password to this file (one line)
- File is gitignored for security
- 📖 Detaillierte Dokumentation: docs/guides/vault-password.md
- Create
Setup Instructions
1. Create Production Secrets
cd deployment/ansible/secrets
# Copy example file
cp production.vault.yml.example production.vault.yml
# Edit with your actual secrets
nano production.vault.yml
# Encrypt the file
ansible-vault encrypt production.vault.yml
# Enter vault password when prompted
2. Store Vault Password
# Create vault password file
echo "your-vault-password-here" > secrets/.vault_pass
# Secure the file
chmod 600 secrets/.vault_pass
📖 Für detaillierte Informationen: Siehe docs/guides/vault-password.md
3. Configure SSH Key
# Generate SSH key if needed
ssh-keygen -t ed25519 -f ~/.ssh/production -C "ansible-deploy"
# Copy public key to production server
ssh-copy-id -i ~/.ssh/production.pub deploy@94.16.110.151
Running Playbooks
Deploy Production Secrets
First-time setup - Deploy secrets to production server:
ansible-playbook playbooks/setup-production-secrets.yml \
--vault-password-file secrets/.vault_pass
Deploy Application Update
Automated via Gitea Actions - Or run manually:
ansible-playbook playbooks/deploy-update.yml \
-e "image_tag=sha-abc123" \
-e "git_commit_sha=abc123" \
-e "deployment_timestamp=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
-e "docker_registry_username=gitea-user" \
-e "docker_registry_password=your-registry-password"
Rollback Deployment
Rollback to previous version:
ansible-playbook playbooks/rollback.yml
Rollback to specific version:
ansible-playbook playbooks/rollback.yml \
-e "rollback_to_version=2025-01-28T15-30-00"
Setup WireGuard VPN
First-time setup - Install WireGuard VPN server:
ansible-playbook -i inventory/production.yml playbooks/setup-wireguard.yml
Add a client:
ansible-playbook -i inventory/production.yml playbooks/add-wireguard-client.yml \
-e "client_name=myclient"
Siehe playbooks/README-WIREGUARD.md für detaillierte Anleitung.
System Maintenance ausführen
Führt die system-Rolle aus, aktualisiert Paketquellen, führt OS-Upgrades durch und aktiviert optional Unattended-Upgrades.
ansible-playbook -i inventory/production.yml \
playbooks/system-maintenance.yml
Tipp: Mit --check lässt sich zunächst ein Dry-Run starten, um anstehende Updates zu prüfen.
Ansible Vault Operations
View Encrypted File
ansible-vault view secrets/production.vault.yml \
--vault-password-file secrets/.vault_pass
Edit Encrypted File
ansible-vault edit secrets/production.vault.yml \
--vault-password-file secrets/.vault_pass
Change Vault Password
ansible-vault rekey secrets/production.vault.yml \
--vault-password-file secrets/.vault_pass
Decrypt File (Temporarily)
ansible-vault decrypt secrets/production.vault.yml \
--vault-password-file secrets/.vault_pass
# DO NOT COMMIT DECRYPTED FILE!
# Re-encrypt when done
ansible-vault encrypt secrets/production.vault.yml \
--vault-password-file secrets/.vault_pass
Testing Playbooks
Test with Check Mode (Dry Run)
ansible-playbook playbooks/deploy-update.yml \
--check \
-e "image_tag=test"
Test Connection
ansible production -m ping
Verify Inventory
ansible-inventory --list -y
System Maintenance Dry Run
ansible-playbook -i inventory/production.yml \
playbooks/system-maintenance.yml \
--check
Security Best Practices
-
Never commit unencrypted secrets
production.vault.ymlmust be encrypted.vault_passis gitignored- Use
.examplefiles for documentation
-
Rotate secrets regularly
- Update vault file
- Re-run
setup-production-secrets.yml - Restart affected services
-
Limit SSH key access
- Use separate SSH key for Ansible
- Limit key to
deployuser only - Consider IP restrictions
-
Vault password security
- Store vault password in secure password manager
- Don't share via insecure channels
- Use different passwords for dev/staging/prod
Troubleshooting
Vault Decryption Failed
Error: Decryption failed (no vault secrets were found)
Solution:
# Verify vault password is correct
ansible-vault view secrets/production.vault.yml \
--vault-password-file secrets/.vault_pass
# If password is wrong, you'll need the original password to decrypt
SSH Connection Failed
Error: Failed to connect to the host
Solutions:
# Test SSH connection manually
ssh -i ~/.ssh/production deploy@94.16.110.151
# Check SSH key permissions
chmod 600 ~/.ssh/production
chmod 644 ~/.ssh/production.pub
# Verify SSH key is added to server
ssh-copy-id -i ~/.ssh/production.pub deploy@94.16.110.151
Docker Registry Authentication Failed
Error: unauthorized: authentication required
Solution:
# Verify registry credentials in vault file
ansible-vault view secrets/production.vault.yml \
--vault-password-file secrets/.vault_pass
# Test registry login manually on production server
docker login registry.michaelschiemer.de
Service Not Starting
Check service logs:
# SSH to production server
ssh -i ~/.ssh/production deploy@94.16.110.151
# Check Docker Compose service logs
docker compose -f {{ app_stack_path }}/docker-compose.yml logs app
docker compose -f {{ app_stack_path }}/docker-compose.yml logs nginx
# Check stack status
docker compose -f {{ app_stack_path }}/docker-compose.yml ps
CI/CD Integration
These playbooks are automatically executed by Gitea Actions workflows:
.gitea/workflows/production-deploy.yml- Callsdeploy-update.ymlon push to main.gitea/workflows/update-production-secrets.yml- Callssetup-production-secrets.ymlon manual trigger.gitea/workflows/system-maintenance.yml- Führtsystem-maintenance.ymlgeplant oder manuell aus, um Pakete aktuell zu halten
Vault password is stored as Gitea Actions secret: ANSIBLE_VAULT_PASSWORD
Inventory Variables
All zentralen Variablen werden in group_vars/production.yml gepflegt und können bei Bedarf im Inventory überschrieben werden. Häufig verwendete Werte:
| Variable | Beschreibung | Standardwert |
|---|---|---|
deploy_user_home |
Home-Verzeichnis des Deploy-Users | /home/deploy |
stacks_base_path |
Basispfad für Docker Compose Stacks | /home/deploy/deployment/stacks |
app_stack_path |
Pfad zum Application Stack | /home/deploy/deployment/stacks/application |
backups_path |
Ablageort für Deployment-Backups | /home/deploy/deployment/backups |
docker_registry |
Interner Registry-Endpunkt (lokal) | localhost:5000 |
docker_registry_external |
Externer Registry-Endpunkt | registry.michaelschiemer.de |
app_domain |
Produktions-Domain | michaelschiemer.de |
health_check_url |
Health-Check Endpoint | https://michaelschiemer.de/health |
max_rollback_versions |
Anzahl vorgehaltener Backups | 5 |
system_update_packages |
Aktiviert OS-Paketupdates via system-Rolle |
true |
system_apt_upgrade |
Wert für apt upgrade (z. B. dist) |
dist |
system_enable_unattended_upgrades |
Aktiviert unattended-upgrades |
true |
system_enable_unattended_reboot |
Steuert automatische Reboots nach Updates | false |
system_unattended_reboot_time |
Reboot-Zeitfenster (wenn aktiviert) | 02:00 |
system_enable_unattended_timer |
Aktiviert Systemd-Timer für apt | true |
system_enable_docker_prune |
Führt nach Updates docker system prune aus |
false |
Backup Management
Backups are automatically created before each deployment:
- Location:
/home/deploy/backups/ - Retention: Last 5 versions kept
- Contents:
current_image.txt- Previously deployed imagestack_status.txt- Stack status before deploymentdeployment_metadata.txt- Deployment details
List Available Backups
ssh -i ~/.ssh/production deploy@94.16.110.151 \
"ls -lh /home/deploy/backups/"
Manual Backup
ansible-playbook playbooks/deploy-update.yml \
--tags backup \
-e "image_tag=current"
Support
For issues with:
- Playbooks: Check this README and playbook comments
- Vault: See Ansible Vault documentation
- Deployment: Review Gitea Actions logs
- Production: SSH to server and check Docker logs