Files
playbooks/nextcloud/roles/nextcloud/tasks/main.yaml
T
tanshu c3e0e4989c Do not create a host file for the entrypoint's PHP session config
The role created ~/podman-data/nextcloud/redis-session.ini and the unit bind-mounted
it over the image path, purely because the entrypoint cannot write there itself.
Removed: the task, the mount, and -- because it is the only thing that triggers the
write -- REDIS_HOST from the environment file. The entrypoint now skips
configure_redis_session entirely and starts.

The Redis attach stays. The valkey role still puts nextcloud_net on the cache
container with the alias `redis`, so an instance whose config.php names that host
reaches it. Writing that configuration is config.php's business, and the readme
gives the three `occ config:system:set` lines for anyone who wants it, including
that this playbook will not do it for them.

The only thing lost is PHP session storage, which falls back to the container's own
filesystem -- a redeploy logs users out. That is the trade for not shipping a file
whose sole purpose is to work around a directory the image owns as root.

The env template and the valkey role comment both say why REDIS_HOST is absent, so
that adding it back is recognisable as the regression it is.
2026-10-16 15:44:33 +05:30

108 lines
4.2 KiB
YAML

---
# Nextcloud as a rootless Podman Quadlet unit.
#
# Everything runs as the connection user; requires gather_facts (set in
# playbook.yml), which is where podman_user and the home directory come from.
#
# A fresh webroot is a valid state. The image initialises the code and serves the
# setup wizard, and this play succeeds. Set nextcloud_admin_user and
# nextcloud_admin_password in vars/default.yml to have it installed unattended
# instead.
- name: Ensure the webroot exists
ansible.builtin.file:
path: "{{ host_directory }}"
state: directory
owner: "{{ podman_user }}"
group: "{{ podman_user }}"
mode: '0755'
# Read by the entrypoint, not by Ansible, so it has to exist before the unit
# starts. Mode 0600: POSTGRES_PASSWORD is in it, and the unit file is
# world-readable.
- name: Write the environment file
ansible.builtin.template:
src: nextcloud.env.j2
dest: "{{ env_file }}"
owner: "{{ podman_user }}"
group: "{{ podman_user }}"
mode: '0600'
notify: Restart Nextcloud
- name: Install the Quadlet unit
ansible.builtin.template:
src: nextcloud.container.j2
dest: "{{ quadlet_dir }}/{{ container_name }}.container"
owner: "{{ podman_user }}"
group: "{{ podman_user }}"
mode: '0644'
notify:
- Reload systemd user units
- Restart Nextcloud
# Quadlet applies [Install] WantedBy= when it generates the unit, so a generated
# unit is never `systemctl enable`d.
- name: Start Nextcloud and wait for it to serve
block:
- name: Start Nextcloud
ansible.builtin.systemd_service:
name: "{{ unit_name }}"
scope: user
state: started
daemon_reload: true
# `systemctl start` returns while the unit is still `activating`, and a
# crash-looping container is `activating` most of the time as well, so
# is-active proves nothing. status.php is Nextcloud's own endpoint: it answers
# only once the entrypoint has finished (including any `occ upgrade`) and
# Apache is up, it reports `installed` either way — so one gate fits both a
# fresh webroot and an update — and `maintenance` is in it too, because a
# maintenance-mode 503 on every page is otherwise invisible to a probe.
- name: Wait for Nextcloud to serve status.php
ansible.builtin.command: >-
podman exec {{ container_name }} php -r
'$b=@file_get_contents("http://127.0.0.1/status.php"); if ($b === false) exit(1);
$j=json_decode($b, true); if (!is_array($j) || !array_key_exists("installed", $j)) exit(1);
exit(empty($j["maintenance"]) ? 0 : 1);'
register: nextcloud_serving
until: nextcloud_serving.rc == 0
retries: 60
delay: 5
changed_when: false
rescue:
# systemd reports "control process exited with error code" and nothing else.
# The reason is in the entrypoint's own output.
- name: Collect diagnostics
ansible.builtin.command: "{{ item }}"
loop:
- "systemctl --user status {{ unit_name }} --no-pager -l"
- "journalctl --user -u {{ unit_name }} --no-pager -n 60"
- "podman logs --tail 60 {{ container_name }}"
register: nextcloud_diag
changed_when: false
failed_when: false
- name: Show them
ansible.builtin.debug:
msg: "{{ nextcloud_diag.results | map(attribute='stdout') | list }}"
- name: Fail
ansible.builtin.fail:
msg: >-
Nextcloud is not serving — see the diagnostics above. A "Permission
denied" under /usr/local/etc/php/conf.d, anything about `db` or `redis`,
and "downgrading is not supported" each point somewhere different.
# Nextcloud wants `cron.php` every five minutes. This has to be *this user's*
# crontab, not root's: root cannot `podman exec` into another user's rootless
# containers. It works without any environment plumbing because the caddy
# playbook enables lingering, so /run/user/<uid> exists whether or not anyone is
# logged in — which is where podman keeps the state `exec` needs.
- name: Schedule Nextcloud's background jobs
ansible.builtin.cron:
name: Nextcloud background jobs
minute: "{{ cron_minute }}"
job: >-
/usr/bin/podman exec -u www-data {{ container_name }}
php {{ webroot }}/cron.php