diff --git a/README.md b/README.md index a8216f7..b502ede 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,7 @@ shared shape, so each new service is a few lines of YAML instead of a copy-paste server block. **Not for production.** No Let's Encrypt automation, no per-vhost cert provisioning, -no cleanup for sites removed from inventory, snakeoil cert as default. Override what -you need. +snakeoil cert as default. Override what you need. Tested on Debian (bookworm/trixie) and Ubuntu (jammy/noble). @@ -78,6 +77,7 @@ uses the shared cert, and proxies everything to `192.168.1.10:8080`. | `nginx_resolver_timeout` | `5s` | | | `nginx_site_enabled_by_default` | `true` | Whether sites without an explicit `enabled` get symlinked. | | `nginx_delete_default_site_config` | `false` | Delete Debian's `sites-available/default` (only matters if you remove the `default` entry from `nginx_sites`). | +| `nginx_prune_sites` | `false` | Remove any file in `sites-available/` or `sites-enabled/` whose basename isn't in `nginx_sites`. Off by default so hand-placed configs survive. | | `nginx_site_config_template` | `nginx-site.conf.j2` | Default template used when a site has no `template_path`/`conf_file`.| | `nginx_sites` | one entry (the catch-all `default` site) | Your list of sites. | | `nginx_snippets` | `[proxy-headers, ssl-params, websockets, fastcgi-php]` | Static snippets dropped into `/etc/nginx/snippets/`. | @@ -125,9 +125,12 @@ The role copies it verbatim into `sites-available/`. - **One shared cert by default.** A single wildcard is the assumed homelab pattern. Per-site override via `ssl_certificate` / `ssl_certificate_key` works for the exceptions. -- **No removal cleanup.** If you delete a site from `nginx_sites`, the existing - `sites-available/` and `sites-enabled/` are not removed. Clean up - manually or set `enabled: false` and let the symlink task remove the link. +- **No removal cleanup by default.** If you delete a site from `nginx_sites`, the + existing `sites-available/` and `sites-enabled/` are left in place. + Set `nginx_prune_sites: true` to have the role remove any file in those + directories whose basename isn't in `nginx_sites` — but note this will also + wipe hand-placed configs the role didn't create (including + `sites-available/default` unless you keep the `default` entry in `nginx_sites`). - **Debian/Ubuntu only.** Paths assume the Debian-style `sites-available/` + `sites-enabled/` split. Won't work on Alpine, RHEL, etc. without surgery. - **No Let's Encrypt / ACME.** Bring your own certs. diff --git a/defaults/main.yaml b/defaults/main.yaml index 383f6ec..54234ed 100755 --- a/defaults/main.yaml +++ b/defaults/main.yaml @@ -10,6 +10,11 @@ nginx_conf_d_path: /etc/nginx/conf.d nginx_delete_default_site_config: false nginx_site_config_extra_options: "" +# When true, any file in nginx_site_config_path or nginx_site_enabled_path +# whose basename is not listed in nginx_sites will be removed. Off by +# default so hand-placed configs aren't wiped by a role run. +nginx_prune_sites: false + # Default SSL cert used by all sites unless overridden per-site # via item.ssl_certificate / item.ssl_certificate_key. Points at the # Debian snakeoil cert (shipped by the ssl-cert package, an nginx dep) diff --git a/tasks/configure_nginx.yaml b/tasks/configure_nginx.yaml index 9ca4216..7a61694 100755 --- a/tasks/configure_nginx.yaml +++ b/tasks/configure_nginx.yaml @@ -8,6 +8,48 @@ - Validate nginx configuration - Reload nginx +- name: Find existing site configs + ansible.builtin.find: + paths: "{{ nginx_site_config_path }}" + file_type: file + register: nginx_existing_site_configs + when: nginx_prune_sites + +- name: Remove site configs no longer in nginx_sites + ansible.builtin.file: + path: "{{ item.path }}" + state: absent + loop: "{{ nginx_existing_site_configs.files | default([]) }}" + loop_control: + label: "{{ item.path | basename }}" + when: + - nginx_prune_sites + - (item.path | basename) not in (nginx_sites | map(attribute='name') | list) + notify: + - Validate nginx configuration + - Reload nginx + +- name: Find existing enabled-site symlinks + ansible.builtin.find: + paths: "{{ nginx_site_enabled_path }}" + file_type: any + register: nginx_existing_enabled_sites + when: nginx_prune_sites + +- name: Remove enabled-site symlinks no longer in nginx_sites + ansible.builtin.file: + path: "{{ item.path }}" + state: absent + loop: "{{ nginx_existing_enabled_sites.files | default([]) }}" + loop_control: + label: "{{ item.path | basename }}" + when: + - nginx_prune_sites + - (item.path | basename) not in (nginx_sites | map(attribute='name') | list) + notify: + - Validate nginx configuration + - Reload nginx + - name: Add all the template site configurations ansible.builtin.template: src: "{{ item.template_path | default(nginx_site_config_template) }}"