--- - name: Deploy Infrastructure Stacks on Production Server hosts: production become: no gather_facts: yes vars: stacks_base_path: "~/deployment/stacks" wait_timeout: 60 tasks: - name: Check if deployment stacks directory exists stat: path: "{{ stacks_base_path }}" register: stacks_dir - name: Fail if stacks directory doesn't exist fail: msg: "Deployment stacks directory not found at {{ stacks_base_path }}" when: not stacks_dir.stat.exists # Create external network required by all stacks - name: Create traefik-public network community.docker.docker_network: name: traefik-public driver: bridge state: present # 1. Deploy Traefik (Reverse Proxy & SSL) - name: Deploy Traefik stack community.docker.docker_compose_v2: project_src: "{{ stacks_base_path }}/traefik" state: present pull: always register: traefik_output - name: Wait for Traefik to be ready wait_for: timeout: "{{ wait_timeout }}" when: traefik_output.changed - name: Check Traefik logs for readiness hint shell: docker compose logs traefik 2>&1 | grep -Ei "(configuration loaded|traefik[[:space:]]v|Starting provider|Server configuration loaded)" || true args: chdir: "{{ stacks_base_path }}/traefik" register: traefik_logs until: traefik_logs.stdout != "" retries: 6 delay: 10 changed_when: false ignore_errors: yes # 2. Deploy PostgreSQL (Database) - name: Deploy PostgreSQL stack community.docker.docker_compose_v2: project_src: "{{ stacks_base_path }}/postgresql" state: present pull: always register: postgres_output - name: Wait for PostgreSQL to be ready wait_for: timeout: "{{ wait_timeout }}" when: postgres_output.changed - name: Check PostgreSQL logs for readiness shell: docker compose logs postgres 2>&1 | grep -Ei "(ready to accept connections|database system is ready)" || true args: chdir: "{{ stacks_base_path }}/postgresql" register: postgres_logs until: postgres_logs.stdout != "" retries: 6 delay: 10 changed_when: false ignore_errors: yes # 3. Deploy Docker Registry (Private Registry) - name: Ensure Registry auth directory exists file: path: "{{ stacks_base_path }}/registry/auth" state: directory mode: '0755' become: yes - name: Create Registry htpasswd file if missing shell: | if [ ! -f {{ stacks_base_path }}/registry/auth/htpasswd ]; then docker run --rm --entrypoint htpasswd httpd:2 -Bbn admin registry-secure-password-2025 > {{ stacks_base_path }}/registry/auth/htpasswd chmod 644 {{ stacks_base_path }}/registry/auth/htpasswd fi args: executable: /bin/bash become: yes changed_when: true register: registry_auth_created - name: Deploy Docker Registry stack community.docker.docker_compose_v2: project_src: "{{ stacks_base_path }}/registry" state: present pull: always register: registry_output - name: Wait for Docker Registry to be ready wait_for: timeout: "{{ wait_timeout }}" when: registry_output.changed - name: Check Registry logs for readiness shell: docker compose logs registry 2>&1 | grep -Ei "(listening on|listening at|http server)" || true args: chdir: "{{ stacks_base_path }}/registry" register: registry_logs until: registry_logs.stdout != "" retries: 6 delay: 10 changed_when: false ignore_errors: yes - name: Verify Registry is accessible uri: url: "http://127.0.0.1:5000/v2/_catalog" user: admin password: registry-secure-password-2025 status_code: 200 timeout: 5 register: registry_check ignore_errors: yes changed_when: false - name: Display Registry status debug: msg: "Registry accessibility: {{ 'SUCCESS' if registry_check.status == 200 else 'FAILED - may need manual check' }}" # 4. Deploy Gitea (CRITICAL - Git Server + MySQL + Redis) - name: Deploy Gitea stack community.docker.docker_compose_v2: project_src: "{{ stacks_base_path }}/gitea" state: present pull: always register: gitea_output - name: Wait for Gitea to be ready wait_for: timeout: "{{ wait_timeout }}" when: gitea_output.changed - name: Check Gitea logs for readiness shell: docker compose logs gitea 2>&1 | grep -Ei "(Listen:|Server is running|Starting server)" || true args: chdir: "{{ stacks_base_path }}/gitea" register: gitea_logs until: gitea_logs.stdout != "" retries: 12 delay: 10 changed_when: false ignore_errors: yes # 5. Deploy Monitoring (Portainer + Grafana + Prometheus) - name: Deploy Monitoring stack community.docker.docker_compose_v2: project_src: "{{ stacks_base_path }}/monitoring" state: present pull: always register: monitoring_output - name: Wait for Monitoring to be ready wait_for: timeout: "{{ wait_timeout }}" when: monitoring_output.changed # Verification - name: List all running containers command: > docker ps --format 'table {{ "{{" }}.Names{{ "}}" }}\t{{ "{{" }}.Status{{ "}}" }}\t{{ "{{" }}.Ports{{ "}}" }}' register: docker_ps_output - name: Display running containers debug: msg: "{{ docker_ps_output.stdout_lines }}" - name: Verify Gitea accessibility via HTTPS uri: url: https://git.michaelschiemer.de method: GET validate_certs: no status_code: 200 timeout: 10 register: gitea_http_check ignore_errors: yes - name: Display Gitea accessibility status debug: msg: "Gitea HTTPS check: {{ 'SUCCESS' if gitea_http_check.status == 200 else 'FAILED - Status: ' + (gitea_http_check.status|string) }}" - name: Summary debug: msg: - "=== Infrastructure Deployment Complete ===" - "Traefik: {{ 'Deployed' if traefik_output.changed else 'Already running' }}" - "PostgreSQL: {{ 'Deployed' if postgres_output.changed else 'Already running' }}" - "Docker Registry: {{ 'Deployed' if registry_output.changed else 'Already running' }}" - "Gitea: {{ 'Deployed' if gitea_output.changed else 'Already running' }}" - "Monitoring: {{ 'Deployed' if monitoring_output.changed else 'Already running' }}" - "" - "Next Steps:" - "1. Access Gitea at: https://git.michaelschiemer.de" - "2. Complete Gitea setup wizard if first-time deployment" - "3. Navigate to Admin > Actions > Runners to get registration token" - "4. Continue with Phase 1 - Gitea Runner Setup"