--- # Publish an app through nginx-proxy. Usage: # # - ansible.builtin.include_role: # name: nginx-proxy # tasks_from: add_vhost # vars: # vhost_name: paperclip # required, becomes conf.d/.conf # vhost_domain: "{{ paperclip_domain }}" # required # vhost_upstream_port: 3100 # required # vhost_upstream_host: 127.0.0.1 # optional # vhost_proxy_timeout: 60s # optional (use 3600s for WebSockets) # vhost_health_path: /health # optional, proxied without access log # # Order: marker -> certificate -> vhost file -> nginx -t -> reload. # The vhost is only written once the certificate exists, and a vhost that # fails nginx -t is rolled back, so one bad app cannot take the others down. - name: Validate vhost parameters ansible.builtin.assert: that: - vhost_name is defined - vhost_domain is defined - vhost_upstream_port is defined fail_msg: "add_vhost needs vhost_name, vhost_domain and vhost_upstream_port" quiet: true - name: Check that the nginx-proxy containers are running ansible.builtin.command: "docker inspect -f '{% raw %}{{.State.Running}}{% endraw %}' {{ item }}" register: nginx_proxy_running failed_when: nginx_proxy_running.rc != 0 or nginx_proxy_running.stdout | trim != 'true' changed_when: false loop: - "{{ nginx_proxy_nginx_container }}" - "{{ nginx_proxy_certbot_container }}" loop_control: label: "{{ item }}" - name: "Register cert marker for {{ vhost_domain }}" ansible.builtin.file: path: "{{ nginx_proxy_domains_dir }}/{{ vhost_domain }}" state: touch modification_time: preserve access_time: preserve mode: "0644" - name: "Check for existing certificate for {{ vhost_domain }}" ansible.builtin.stat: path: "{{ nginx_proxy_certs_dir }}/live/{{ vhost_domain }}/fullchain.pem" register: nginx_proxy_cert_before # Targeted run: only this domain, so another app's failed issuance # cannot fail this play. - name: "Issue certificate for {{ vhost_domain }}" ansible.builtin.command: >- docker exec {{ nginx_proxy_certbot_container }} /scripts/certbot-init.sh {{ vhost_domain }} register: nginx_proxy_cert_issue changed_when: "'Certificate obtained' in nginx_proxy_cert_issue.stdout" when: not nginx_proxy_cert_before.stat.exists - name: "Confirm certificate exists for {{ vhost_domain }}" ansible.builtin.stat: path: "{{ nginx_proxy_certs_dir }}/live/{{ vhost_domain }}/fullchain.pem" register: nginx_proxy_cert_after failed_when: not nginx_proxy_cert_after.stat.exists - name: "Write {{ vhost_name }} vhost, test, and reload" block: - name: "Template conf.d/{{ vhost_name }}.conf" ansible.builtin.template: src: vhost.conf.j2 dest: "{{ nginx_proxy_conf_dir }}/{{ vhost_name }}.conf" mode: "0644" backup: true register: nginx_proxy_vhost_file - name: Test nginx config ansible.builtin.command: "docker exec {{ nginx_proxy_nginx_container }} nginx -t" changed_when: false - name: Reload nginx ansible.builtin.command: "docker exec {{ nginx_proxy_nginx_container }} nginx -s reload" when: nginx_proxy_vhost_file.changed - name: Remove superseded backup ansible.builtin.file: path: "{{ nginx_proxy_vhost_file.backup_file }}" state: absent when: nginx_proxy_vhost_file.backup_file is defined rescue: - name: Restore previous vhost ansible.builtin.copy: src: "{{ nginx_proxy_vhost_file.backup_file }}" dest: "{{ nginx_proxy_conf_dir }}/{{ vhost_name }}.conf" remote_src: true mode: "0644" when: nginx_proxy_vhost_file.backup_file | default('') | length > 0 - name: Remove backup after restoring it ansible.builtin.file: path: "{{ nginx_proxy_vhost_file.backup_file }}" state: absent when: nginx_proxy_vhost_file.backup_file | default('') | length > 0 - name: Remove the new vhost (there was no previous one) ansible.builtin.file: path: "{{ nginx_proxy_conf_dir }}/{{ vhost_name }}.conf" state: absent when: - nginx_proxy_vhost_file is defined - nginx_proxy_vhost_file.backup_file | default('') | length == 0 - name: Fail with context ansible.builtin.fail: msg: >- nginx rejected the {{ vhost_name }} vhost; it was rolled back and the running config is unchanged.