Make the Nextcloud playbook standalone: first run installs, later runs update
The playbook is meant to be usable on its own, on any host, the way every other one here is: the first run creates the container, and every run after that converges it. It had picked up a migration shape instead -- a readme that assumed a webroot copied in from monoco, a warning that nagged when config.php was missing, and, worse, readiness gates that only passed for an instance that was already installed. That last one was a real bug for the intended use. On a host with no webroot, the image initialises the code, writes no config.php because it has no admin credentials to install with, and serves the setup wizard -- so `occ status` never reports installed: true, and the play would have spent its whole retry budget and then failed on a first run that was going exactly as designed. What replaces it: - The readiness gate is Apache answering status.php inside the container, which is Nextcloud's own endpoint and reports `installed` either way. It is the one thing `is-active` cannot tell us, and it passes for a wizard that has not been completed. - config.php's presence then decides the report: wait for maintenance mode to clear and print `occ status` for an instance, or say what is left for a human to do for a fresh one -- and succeed. The two optional credentials in vars/default.yml install unattended instead, in which case the same endpoints come back installed and the wizard note never appears. - The app creates its own database. Postgres is a shared unit with one default database and nothing else in this repo knows this app's database name, so a fresh cluster previously reached the installer and failed on "database nextcloud does not exist". It is a lookup and a `createdb`, a no-op afterwards, and it fails with the instruction to run postgres/playbook.yml if the cluster is not there rather than with a bare psql error. - Updates. An update in a Quadlet deployment is "pull, then recreate": the unit names a tag, `podman run` reuses the local image, and nothing fetches a newer one. The role pulls on every run and compares the image ID either side, so an unchanged tag is not a change and a running site is not restarted for nothing. - Handlers are flushed before verification, so a queued recreate -- new image, changed unit, changed environment -- happens before the checks rather than after them, and the checks describe what will keep running. Removed: the entire monoco migration section and its copy commands, the config.php warning that existed only to serve it, the host-specific notes, and the narrative comments about this play's first runs. References to the audit and migration planning docs go with them; the ownership reasoning is stated inline, since it does not depend on where an instance came from. `hosts:` is now the only host-specific thing in the directory. The readme is rewritten around the two paths it now has -- "First run" and "Updates" -- with the deploy order, the session-file trap and the debugging commands kept.
This commit is contained in:
1 parent
1038037e45
commit
be53cec757
11 files changed
+511
-672
No files matched your search
+45
-29
@@ -19,6 +19,8 @@ quadlet_dir: "{{ ansible_facts['user_dir'] }}/.config/containers/systemd"
|
||||
# ---------------------------------------------------------------------------
|
||||
# Nextcloud
|
||||
# ---------------------------------------------------------------------------
|
||||
# The one site-specific value: the hostname Caddy serves this instance on and the
|
||||
# hostname that goes into trusted_domains when the install is unattended.
|
||||
host: cloud.hopsngrains.com
|
||||
|
||||
container_name: nextcloud
|
||||
@@ -26,7 +28,7 @@ unit_name: "{{ container_name }}.service"
|
||||
|
||||
# Fully qualified on purpose: rootless Podman resolves short names against
|
||||
# unqualified-search-registries, and there is no TTY to answer a prompt when the
|
||||
# systemd unit starts. The Docker-era playbook said `nextcloud:latest`.
|
||||
# systemd unit starts.
|
||||
image: docker.io/library/nextcloud:latest
|
||||
|
||||
# The Quadlet file is <network_name>.network. caddy/roles/register pins the
|
||||
@@ -36,31 +38,30 @@ network_name: "{{ container_name }}"
|
||||
|
||||
# The image's VOLUME and Apache document root — and, unlike the other services
|
||||
# here, it holds the *code* as well as the state: config.php, data/,
|
||||
# custom_apps/ and every app's PHP files live under it. The Docker-era playbook
|
||||
# bind-mounted the whole thing from /var/lib/nextcloud, and this one keeps that
|
||||
# shape on purpose. See readme.md, "What is state, and what is not".
|
||||
# custom_apps/ and every app's PHP file live under it. See readme.md, "What is
|
||||
# state, and what is not".
|
||||
host_directory: "{{ podman_data_dir }}/{{ container_name }}"
|
||||
webroot: /var/www/html
|
||||
|
||||
# The uid the *server* writes as: 33 is www-data, which owns the webroot, because
|
||||
# the entrypoint rsyncs the code in with --chown www-data:www-data and Apache runs
|
||||
# its workers as that account. `docker exec nextcloud id -u` reported 0 on the old
|
||||
# host, because the entrypoint drops privileges after starting — do not derive the
|
||||
# mapping from that number (docs/podman-compat-audit.md §3.2).
|
||||
# its workers as that account. `docker exec nextcloud id -u` reports 0 in the
|
||||
# upstream image, because the entrypoint drops privileges after starting — do not
|
||||
# derive the mapping from that number (docs/podman-compat-audit.md §3.2).
|
||||
app_uid: 33
|
||||
app_gid: 33
|
||||
|
||||
# The port Apache listens on inside the container, and the one Caddy proxies to.
|
||||
container_port: 80
|
||||
|
||||
# PHP's session-handler config, and the one path in the image this deployment has
|
||||
# to write to. The entrypoint generates the file from REDIS_HOST on every start,
|
||||
# but its target directory (`/usr/local/etc/php/conf.d`) is created by root in the
|
||||
# php base image with no chown, so the uid this container runs as cannot create a
|
||||
# file inside it. Mounting a host-owned file over that exact path is what gives
|
||||
# the write somewhere it is allowed to land, and the template below keeps the
|
||||
# image's own content in it so a refused write is not fatal either. See readme.md,
|
||||
# "The PHP session file".
|
||||
# PHP's session-handler config, and the one path in the image the entrypoint has
|
||||
# to write to. It generates the file from REDIS_HOST on every start, but its
|
||||
# target directory (`/usr/local/etc/php/conf.d`) is created by root in the php
|
||||
# base image with no chown, so the uid this container runs as cannot create a file
|
||||
# inside it. Mounting a host-owned file over that exact path is what gives the
|
||||
# write somewhere it is allowed to land, and the template keeps the image's own
|
||||
# content in it so a refused write is not fatal either. See readme.md, "The PHP
|
||||
# session file".
|
||||
php_conf_dir: "{{ host_directory }}/php-conf.d"
|
||||
php_session_ini: "{{ php_conf_dir }}/redis-session.ini"
|
||||
php_session_ini_mount: /usr/local/etc/php/conf.d/redis-session.ini
|
||||
@@ -76,16 +77,16 @@ env_file: "{{ host_directory }}/nextcloud.env"
|
||||
postgres_container: postgres
|
||||
postgres_unit: "{{ postgres_container }}.service"
|
||||
|
||||
# The credentials and database name the Docker-era playbook embedded in its
|
||||
# `env:` block, kept as-is so the already-restored `nextcloud` database (320 MB,
|
||||
# one of the 12 in postgres/migrate-from-monoco.md) and config.php agree.
|
||||
# The database this instance uses, and the credentials for it. They have to match
|
||||
# whatever the shared Postgres unit was initialised with (postgres/vars/default.yml)
|
||||
# — this play attaches to a database, it does not create one, so the database has
|
||||
# to exist before the first run.
|
||||
pg_user: postgres
|
||||
pg_password: '123456'
|
||||
pg_database: nextcloud
|
||||
|
||||
# `db` is the host config.php actually connects to — the Docker-era container set
|
||||
# POSTGRES_HOST=db, and postgres/roles/attach adds that alias to the shared
|
||||
# Postgres unit.
|
||||
# `db` is the alias postgres/roles/attach adds to the shared Postgres unit, and
|
||||
# the host this instance's config.php connects to.
|
||||
db_host: db
|
||||
|
||||
# The shared Valkey unit this app attaches itself to (valkey/playbook.yml), and
|
||||
@@ -100,21 +101,36 @@ redis_host: redis
|
||||
caddy_name: caddy
|
||||
caddyfile_path: "{{ podman_data_dir }}/{{ caddy_name }}/conf/Caddyfile"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# First-run install (optional)
|
||||
# ---------------------------------------------------------------------------
|
||||
# Leave both empty to install through the web wizard at https://{{ host }} — the
|
||||
# play reports that this is what is left to do and succeeds. Set both to have the
|
||||
# image install unattended on a fresh webroot instead: it runs
|
||||
# `occ maintenance:install` with these credentials and sets trusted_domains to
|
||||
# `host` in the same pass, so the instance is usable immediately.
|
||||
#
|
||||
# They are only consumed while there is no config.php, so they are inert on an
|
||||
# existing instance, and the environment template omits them entirely when they
|
||||
# are empty (an empty NEXTCLOUD_ADMIN_USER would be treated as a value and fail
|
||||
# the install).
|
||||
nextcloud_admin_user: ""
|
||||
nextcloud_admin_password: ""
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Background jobs
|
||||
# ---------------------------------------------------------------------------
|
||||
# Nextcloud wants `cron.php` every five minutes. The Docker-era playbook put it in
|
||||
# *root's* crontab as `docker exec -u www-data nextcloud php cron.php`; root
|
||||
# cannot `podman exec` into another user's rootless containers, so it becomes a
|
||||
# systemd user timer next to the Quadlet units (docs/podman-migration.md §8(b)).
|
||||
# Nextcloud wants `cron.php` every five minutes. It cannot be a crontab entry:
|
||||
# root cannot `podman exec` into another user's rootless containers, and there is
|
||||
# no container user to own a crontab. A systemd user timer next to the Quadlet
|
||||
# units is the equivalent.
|
||||
cron_unit: "{{ container_name }}-cron"
|
||||
cron_schedule: "*:0/5"
|
||||
user_unit_dir: "{{ ansible_facts['user_dir'] }}/.config/systemd/user"
|
||||
|
||||
# The site block, unchanged from the Docker-era vars: Caddy terminates TLS and
|
||||
# Nextcloud has always asked for HSTS. There is no request-body limit to carry
|
||||
# over — Caddy streams bodies and imposes none by default, whereas the dead nginx
|
||||
# front end needed client_max_body_size spelled out.
|
||||
# The site block. Caddy terminates TLS, and Nextcloud asks for HSTS. There is no
|
||||
# request-body limit to carry over: Caddy streams bodies and imposes none by
|
||||
# default.
|
||||
snippet_block: |
|
||||
{{ host }} {
|
||||
reverse_proxy {{ container_name }}:{{ container_port }}
|
||||
|
||||
Reference in new issue
Block a user