- Jinja 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| .forgejo/workflows | ||
| defaults | ||
| files | ||
| meta | ||
| tasks | ||
| templates | ||
| .gitignore | ||
| CHANGELOG.md | ||
| LICENSE | ||
| README.md | ||
Nextcloud
Deploys a Nextcloud instance via Docker Compose with PostgreSQL, Redis, Collabora Online, notify_push, and Imaginary, using Traefik as a reverse proxy.
Requirements
- Docker
community.dockerAnsible collection
Variables
| Variable | Default | Description |
|---|---|---|
nextcloud_instance |
nextcloud |
Unique instance name. Used as Compose project name and container name prefix. Change when running multiple instances on the same host. |
nextcloud_server_id |
1 |
Numeric server ID written to Nextcloud's serverid config key. Useful for distinguishing multiple instances in logs. |
nextcloud_data_path |
/data/nextcloud |
Host path for persistent data (DB, files, ClamAV, Elasticsearch data). |
nextcloud_docker_path |
/docker/nextcloud |
Host path for the compose.yml file and Apache/PHP config snippets. |
nextcloud_version |
33.0.3-apache |
Nextcloud image tag. Pin to a specific version in production. |
nextcloud_db_version |
17 |
PostgreSQL image tag. Pin to a specific version in production. |
nextcloud_redis_version |
alpine |
Redis image tag. |
nextcloud_collabora_version |
latest |
Collabora Online (CODE) image tag. |
nextcloud_notify_push_version |
latest |
notify_push image tag. |
nextcloud_imaginary_version |
latest |
Imaginary image tag. |
nextcloud_clamav_version |
stable |
ClamAV image tag. Only used when nextcloud_clamav_enabled is true. |
nextcloud_elasticsearch_version |
latest |
Elasticsearch (AIO fulltextsearch) image tag. Only used when nextcloud_elasticsearch_enabled is true. |
nextcloud_url |
~ |
Canonical public domain Nextcloud runs on (no protocol prefix). Required. |
nextcloud_extra_domains |
[] |
Additional public domains this instance also answers on. See Extra domains below. |
nextcloud_db_pass |
~ |
PostgreSQL password for the nextcloud user. Required. Generate with openssl rand -hex 32. |
nextcloud_admin_user |
admin |
Admin account username created on first boot. Ignored on subsequent runs. |
nextcloud_admin_password |
~ |
Admin account password. Required. Generate with openssl rand -hex 32. |
nextcloud_collabora_password |
~ |
Collabora Online admin password. Required. |
nextcloud_collabora_url |
~ |
Public domain Collabora runs on (no protocol prefix). Required. |
nextcloud_loglevel |
3 |
Log level: 0=DEBUG 1=INFO 2=WARNING 3=ERROR 4=FATAL. Defaults to ERROR since Docker deployments generate harmless recurring warnings. |
nextcloud_windows_compatible_filenames |
false |
Enforce Windows-compatible filenames. Enable when sharing files with Windows clients. |
nextcloud_sanitize_filenames |
false |
Rename all existing files that violate current filename constraints. Set once after enabling nextcloud_windows_compatible_filenames, then reset to false. Can be slow on large instances. |
nextcloud_data_location |
"" |
Displayed in the Privacy admin panel under "Where is your data?" (e.g. "Germany"). Not shown when empty. |
nextcloud_full_disk_encryption |
false |
Shows the "This server is using full-disk-encryption" notice in the Privacy admin panel. |
nextcloud_phone_region |
DE |
Default phone region for number formatting (ISO 3166-1 alpha-2). |
nextcloud_maintenance_window |
1 |
Hour (0–23 UTC) when Nextcloud's maintenance window starts (background jobs, updates). |
nextcloud_default_language |
~ |
Default language for users without their own setting, e.g. de_DE (formal German) or de. Null follows the browser. |
nextcloud_default_locale |
~ |
Default locale (date/number formats), e.g. de_DE. Null derives it from the language. |
nextcloud_mounts |
[] |
Extra volume mounts appended to the Nextcloud app container. See Extra mounts below. |
nextcloud_apps |
[] |
Extra Nextcloud Store app IDs to install (e.g. calendar, contacts). See Extra apps below. |
nextcloud_imaginary_memory_limit |
2g |
Memory limit for the Imaginary image-conversion container. |
nextcloud_clamav_enabled |
false |
Deploys a ClamAV container and configures Nextcloud to scan uploads. |
nextcloud_elasticsearch_enabled |
false |
Deploys an Elasticsearch container and configures full-text search. |
nextcloud_elasticsearch_memory |
1G |
JVM heap size for Elasticsearch (passed as both -Xms and -Xmx). |
nextcloud_elasticsearch_reindex |
false |
Triggers a full re-index of all files on each playbook run. Expensive — only set temporarily when rebuilding the search index. |
nextcloud_mail_conf |
~ |
SMTP configuration. See Mail below. |
nextcloud_oidc_conf |
~ |
OpenID Connect login (user_oidc app). See OpenID Connect below. |
nextcloud_extra_hosts |
{} |
Extra /etc/hosts entries (hostname: IP) for the app and Collabora containers. See Extra hosts below. |
nextcloud_talk_signaling_url |
~ |
HPB signaling server hostname (no protocol), e.g. signal.example.com. Setting this installs Talk and configures signaling. Requires nextcloud_talk_signaling_secret. |
nextcloud_talk_signaling_secret |
~ |
Shared secret for the HPB signaling server. |
nextcloud_talk_stun_server |
~ |
STUN server in hostname:port format. Replaces all existing STUN servers (including the Nextcloud default). |
nextcloud_talk_turn_server |
~ |
TURN server in hostname:port format. Requires nextcloud_talk_turn_secret. |
nextcloud_talk_turn_secret |
~ |
Shared secret for the TURN server. |
nextcloud_talk_recording_url |
~ |
Recording backend full URL, e.g. https://recording.example.com. Requires nextcloud_talk_recording_secret. |
nextcloud_talk_recording_secret |
~ |
Shared secret for the recording backend. |
nextcloud_uninstall |
false |
Stops and removes containers and the compose directory. |
nextcloud_purge |
false |
Also removes the data directory. Requires nextcloud_uninstall: true. Irreversible. |
Usage
Minimal setup:
nextcloud_url: "cloud.example.com"
nextcloud_collabora_url: "office.example.com"
nextcloud_db_pass: "{{ vault_nextcloud_db_pass }}"
nextcloud_admin_password: "{{ vault_nextcloud_admin_password }}"
nextcloud_collabora_password: "{{ vault_nextcloud_collabora_password }}"
Multiple instances on the same host:
# instance 1
nextcloud_instance: nextcloud_personal
nextcloud_url: cloud.example.com
nextcloud_collabora_url: office.example.com
nextcloud_db_pass: "secret_a"
nextcloud_collabora_password: "collab_secret_a"
nextcloud_data_path: /data/nextcloud_personal
nextcloud_docker_path: /docker/nextcloud_personal
# instance 2
nextcloud_instance: nextcloud_team
nextcloud_url: team.example.com
nextcloud_collabora_url: office-team.example.com
nextcloud_db_pass: "secret_b"
nextcloud_collabora_password: "collab_secret_b"
nextcloud_data_path: /data/nextcloud_team
nextcloud_docker_path: /docker/nextcloud_team
When nextcloud_mail_conf is set, Nextcloud's SMTP mailer is configured. Required keys: host, username, password.
nextcloud_mail_conf:
host: smtp.example.com
username: support@example.com
password: secret
address: support # optional, defaults to the part before @ in username
domain: example.com # optional, defaults to the part after @ in username
port: 587 # optional, default: 587
security: tls # optional, default: tls (options: tls, ssl, none)
Extra domains
nextcloud_extra_domains adds hostnames this same instance also answers on — e.g. a customer's own vanity domain pointed at the same backend:
nextcloud_url: demo.tni.dev
nextcloud_extra_domains:
- demo.inocloud.it
Each extra domain is added to NEXTCLOUD_TRUSTED_DOMAINS (else Nextcloud rejects the request with "Access through untrusted domain") and OR'd into the Traefik Host() rule, so Traefik requests and renews its own certificate per domain. nextcloud_url stays canonical — OVERWRITECLIURL, ServerName, and the notify_push setup URL are unaffected, so links Nextcloud generates itself always use nextcloud_url regardless of which domain a request came in on.
Extra mounts
Additional volumes can be injected into the Nextcloud app container using nextcloud_mounts. Provide the value as a YAML block scalar — it is indented directly into the volumes list:
nextcloud_mounts: |
- /mnt/nas/photos:/mnt/nas/photos:ro
- /mnt/nas/documents:/mnt/nas/documents:ro
Extra apps
nextcloud_apps installs additional apps from the Nextcloud Store by app ID, for anything not already covered by this role's own feature flags (Talk, ClamAV, Elasticsearch, Collabora):
nextcloud_apps:
- calendar
- contacts
- tasks
Each is installed with occ app:install (idempotent — already-installed apps are skipped) and left at its default configuration. Apps this role configures itself (notify_push, richdocuments, files_antivirus, spreed, the Elasticsearch bundle) don't need to be listed here — installing them via nextcloud_apps too would just be a redundant no-op alongside this role's own install.
ClamAV
Set nextcloud_clamav_enabled: true to deploy a ClamAV container alongside Nextcloud. The files_antivirus app is installed and configured automatically. ClamAV performs an initial signature database download on first start, which may take several minutes before it becomes healthy.
Elasticsearch
Set nextcloud_elasticsearch_enabled: true to deploy a full-text search stack (fulltextsearch, fulltextsearch_elasticsearch, files_fulltextsearch apps). The Elasticsearch container uses the nextcloud/aio-fulltextsearch image. Tune memory with nextcloud_elasticsearch_memory (default 1G).
To force a full re-index:
nextcloud_elasticsearch_reindex: true
Reset to false after the run — leaving it true re-indexes every playbook run.
OpenID Connect
nextcloud_oidc_conf installs the user_oidc app and configures one provider. By default it becomes the only way to log in: /login redirects straight to the provider, the password form is hidden and the lost password link is disabled. Local accounts (e.g. nextcloud_admin_user) keep working as a break-glass path at /login?direct=1. Logging out of Nextcloud also ends the session at the provider. Desktop and mobile clients log in through the browser, so they use the provider too.
| Key | Required | Default | Description |
|---|---|---|---|
discovery_uri |
yes | — | Full URL of the provider's .well-known/openid-configuration. |
client_id |
yes | — | Client ID. |
client_secret |
yes | — | Client secret. |
identifier |
no | sso |
Provider name in Nextcloud, shown on the login button when only is false. |
only |
no | true |
Redirect /login to the provider and disable the lost password link. |
scope |
no | openid email profile |
Requested scopes. |
mapping_uid |
no | preferred_username |
Claim used as the Nextcloud user ID. |
mapping_display_name |
no | name |
Claim used as the display name. |
mapping_email |
no | email |
Claim used as the email address. |
mapping_groups |
no | unset | Claim holding the user's groups. Enables group provisioning; the values become group IDs, so admin grants admin rights. |
group_whitelist_regex |
no | unset | Only provision (and remove) groups matching this regex. |
pkce |
no | true |
Use PKCE for the authorization code flow. |
mapping_language |
no | unset | Claim with the user's language, e.g. locale from Keycloak. Applied on each login. |
Example with Keycloak (client from the keycloak role's keycloak_oidc_clients with roles: [admin]):
nextcloud_oidc_conf:
discovery_uri: https://auth.example.com/realms/example/.well-known/openid-configuration
client_id: nextcloud
client_secret: "{{ vault_nextcloud_oidc_secret }}"
mapping_groups: roles
group_whitelist_regex: "^admin$"
Extra hosts
Nextcloud calls its own public URL (Collabora's WOPI callbacks, setup checks) and, with nextcloud_oidc_conf, the provider. When the host can't reach its own public IP (no NAT hairpin on the firewall), pin these names to an internal address that serves them, e.g. the reverse proxy in front:
nextcloud_extra_hosts:
cloud.example.com: 10.0.0.2
office.example.com: 10.0.0.2
auth.example.com: 10.0.0.2
While set, Nextcloud's dns_pinning is turned off: Nextcloud otherwise resolves hostnames itself and ignores /etc/hosts.
Talk
Setting nextcloud_talk_signaling_url installs the Talk (spreed) app and configures the HPB signaling server. STUN, TURN, and recording are independently optional.
Signaling (HPB) — required to enable Talk
nextcloud_talk_signaling_url: signal.example.com
nextcloud_talk_signaling_secret: "{{ vault_talk_signaling_secret }}"
STUN
Replaces all current STUN servers (including the Nextcloud default stun.nextcloud.com:443):
nextcloud_talk_stun_server: turn.example.com:3478
TURN
Configures a TURN server (with turn,turns schemes and udp,tcp protocols):
nextcloud_talk_turn_server: turn.example.com:3478
nextcloud_talk_turn_secret: "{{ vault_talk_turn_secret }}"
Recording backend
nextcloud_talk_recording_url: https://recording.example.com
nextcloud_talk_recording_secret: "{{ vault_talk_recording_secret }}"
Full example combining all features (coturn co-located with HPB is a common setup):
nextcloud_talk_enabled: true
nextcloud_talk_signaling_url: signal.example.com
nextcloud_talk_signaling_secret: "{{ vault_talk_signaling_secret }}"
nextcloud_talk_stun_server: signal.example.com:3478
nextcloud_talk_turn_server: signal.example.com:3478
nextcloud_talk_turn_secret: "{{ vault_talk_turn_secret }}"
Uninstall
To stop and remove all containers and the compose directory:
nextcloud_uninstall: true
To also wipe all data including uploaded files and the database (irreversible):
nextcloud_uninstall: true
nextcloud_purge: true
Notes
- Nextcloud and Collabora are both Traefik-only — there is no port exposure option. Both
nextcloud_urlandnextcloud_collabora_urlare required. - notify_push is deployed automatically and routed via Traefik at
/pushonnextcloud_urland everynextcloud_extra_domainsentry. - Imaginary handles image preview generation. It is always deployed alongside Nextcloud.
- Data directory ownership:
db/is owned by999:999mode0700(PostgreSQL),data/andtmp/by33:33(www-data/Nextcloud),elasticsearch/by1000:0,clamav/byroot. - A host cron job is installed at
/etc/cron.d/{{ nextcloud_instance }}-nextcloudthat runscron.phpevery 5 minutes aswww-data. - PHP upload limits are set to 200 GiB via a mounted
zzz-upload-size.ini. Apache's body limit is disabled (APACHE_BODY_LIMIT=0) so PHP limits are the effective ceiling.