No description
Find a file
2026-08-11 22:03:43 +02:00
.gitignore add container and pod quadlets 2026-07-31 16:20:20 +02:00
euro-office.container euro office instead of only office 2026-08-11 21:12:18 +02:00
LICENSE add container and pod quadlets 2026-07-31 16:20:20 +02:00
nextcloud-app.container add container and pod quadlets 2026-07-31 16:20:20 +02:00
nextcloud-caddy.container add container and pod quadlets 2026-07-31 16:20:20 +02:00
nextcloud-db.container add container and pod quadlets 2026-07-31 16:20:20 +02:00
nextcloud-redis.container go back to password in cmd approach 2026-07-31 16:44:07 +02:00
nextcloud.pod remove nextcloud 2026-08-11 22:03:43 +02:00
README.md add container and pod quadlets 2026-07-31 16:20:20 +02:00

Nextcloud podman quadlet setup

Where these files go

Copy all *.pod and *.container files to:

~/.config/containers/systemd/

Then run systemctl --user daemon-reload and systemctl --user start nextcloud-pod.service (the pod's generated unit name is <pod-basename>-pod.service, e.g. nextcloud-pod.service).

1. Fix the host paths first

Your original YAML mixed relative hostPaths (db, html, caddy/caddy_data, caddy/Caddyfile, syncthing_config) with absolute ones (/mnt/nextcloud_data, ...). Quadlets need absolute paths — there's no "current directory" once systemd runs these as a service. I mapped the relative ones under %h/nextcloud-app/ (i.e. $HOME/nextcloud-app/...) as a placeholder:

~/nextcloud-app/db
~/nextcloud-app/html
~/nextcloud-app/caddy/caddy_data
~/nextcloud-app/caddy/Caddyfile
~/nextcloud-app/syncthing_config

Edit the Volume= lines in each .container file to point at wherever these actually live on your host, then create the directories / move existing data into place before first start.

The two paths that were already absolute (/mnt/nextcloud_data and /mnt/nextcloud_data/j.p.hagedoorn/files) were kept as-is.

2. SELinux labels preserved

  • :Z (private, unshared label) — db, syncthing_config, caddy_data, Caddyfile: only one container touches each of these.
  • :z (shared label) — html and data: both nextcloud-app and nextcloud-caddy (or nextcloud-syncthing) need access to the same directory, so it must be shared.

This mirrors your original manifest exactly.

3. Secrets (individual podman secrets, matching your forgejo pattern)

Your original k8s manifest used envFrom: secretRef to inject a whole set of key/value pairs at once (e.g. MYSQL_ROOT_PASSWORD, MYSQL_DATABASE, MYSQL_USER, MYSQL_PASSWORD for nextcloud-db-secrets; REDIS_HOST_PASSWORD for nextcloud-redis-secrets). Following the same approach as your forgejo pod, each value now gets its own podman secret, created from a plaintext file on the host via ExecStartPre= in nextcloud.pod, and injected with Secret=name,type=env,target=VAR in whichever .container file needs it.

Assumed key names — I used the standard mariadb/nextcloud-image env vars. Adjust the target= values in the .container files if your actual k8s Secrets used different keys:

Secret file Target env var Used by
mysql-root-password.secret MYSQL_ROOT_PASSWORD db only
mysql-database.secret MYSQL_DATABASE db, app
mysql-user.secret MYSQL_USER db, app
mysql-password.secret MYSQL_PASSWORD db, app
redis-password.secret REDIS_HOST_PASSWORD redis, app

Create the source files — each contains just the raw value, no KEY= prefix:

mkdir -p ~/.config/containers/systemd/secrets

echo -n 'super-secret-root-pw'  > ~/.config/containers/systemd/secrets/mysql-root-password.secret
echo -n 'nextcloud'             > ~/.config/containers/systemd/secrets/mysql-database.secret
echo -n 'nextcloud'             > ~/.config/containers/systemd/secrets/mysql-user.secret
echo -n 'super-secret-db-pw'    > ~/.config/containers/systemd/secrets/mysql-password.secret
echo -n 'super-secret-redis-pw' > ~/.config/containers/systemd/secrets/redis-password.secret

chmod 600 ~/.config/containers/systemd/secrets/*.secret

nextcloud.pod's [Service] section runs podman secret create --replace <name> <file> for each of these before the pod starts, so editing a file and restarting the pod (systemctl --user restart nextcloud-pod.service) picks up new values — same workflow as your forgejo database-passwd.secret.

Once a secret is created, podman stores it in its own secrets backend — you can delete the source .secret files afterward if you like, they're only read again on ExecStartPre (i.e. every pod (re)start), so it's simplest to just leave them in place and treat ~/.config/containers/systemd/secrets/ itself as the sensitive directory (keep its permissions tight, e.g. chmod 700 on the directory too).

4. The redis-server --requirepass $(REDIS_HOST_PASSWORD) trick

Kubernetes expands $(VAR) in args for you. Podman's Exec= does not do shell expansion — it passes arguments literally to the entrypoint. nextcloud-redis.container wraps the command in sh -c "..." so the shell performs the substitution at container start, using the REDIS_HOST_PASSWORD value injected via the redis-password.secret podman secret.

5. Startup ordering

Kubernetes pods start all containers together with no ordering guarantees beyond what the app handles via retries. I added explicit After=/Wants= so nextcloud-app waits for the db and redis containers, and nextcloud-caddy waits for nextcloud-app — mostly to avoid noisy failed-connection logs on cold start. Nextcloud/PHP-FPM will still need to retry internally if MariaDB isn't fully ready to accept connections yet (this is normal and not something quadlets alone can fully solve, same as in Kubernetes).

6. Ports

Ports are published at the pod level (nextcloud.pod), not per-container, since all containers in a pod share one network namespace — this is the same model Kubernetes uses. I split syncthing's 22000 into explicit /tcp and /udp and marked 21027 (local discovery) as /udp, since your original YAML didn't specify protocol and syncthing actually needs both.