Deploy Nextcloud file sync/share with automated installation (#4)

This commit implements a complete Nextcloud deployment with PostgreSQL, Redis,
automated installation, and preparation for OIDC/SSO integration with Zitadel.

## Nextcloud Deployment

### New Ansible Role (ansible/roles/nextcloud/)
- Complete Nextcloud v30 deployment with Docker Compose
- PostgreSQL 16 backend with persistent volumes
- Redis 7 for caching and file locking
- Automated installation via Docker environment variables
- Post-installation configuration via occ commands

### Features Implemented
- **Database**: PostgreSQL with proper credentials and persistence
- **Caching**: Redis for memory caching and file locking
- **HTTPS**: Traefik integration with Let's Encrypt SSL
- **Security**: Proper security headers and HSTS
- **WebDAV**: CalDAV/CardDAV redirect middleware
- **Configuration**: Automated trusted domain, reverse proxy, and Redis setup
- **OIDC Preparation**: user_oidc app installed and enabled

### Traefik Updates
- Added Nextcloud routing to dynamic.yml (static file-based config)
- Configured CalDAV/CardDAV redirect middleware
- Added Nextcloud-specific security headers

### Configuration Tasks
- Automated trusted domain configuration for nextcloud.test.vrije.cloud
- Reverse proxy overwrite settings (protocol, host, CLI URL)
- Redis cache and locking configuration
- Default phone region (NL)
- Background jobs via cron

## Deployment Status

 Successfully deployed and tested:
- Nextcloud: https://nextcloud.test.vrije.cloud/
- Admin login working
- PostgreSQL database initialized
- Redis caching operational
- HTTPS with Let's Encrypt SSL
- user_oidc app installed (ready for Zitadel integration)

## Next Steps

To complete OIDC/SSO integration:
1. Create OIDC application in Zitadel console
2. Use redirect URI: https://nextcloud.test.vrije.cloud/apps/user_oidc/code
3. Configure provider in Nextcloud with Zitadel credentials

Partially addresses #4

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Pieter 2026-01-06 09:30:54 +01:00
parent 054e0e1e87
commit 93ce586b94
9 changed files with 327 additions and 2 deletions

View file

@ -26,8 +26,14 @@
zitadel_domain: "{{ client_secrets.zitadel_domain }}" zitadel_domain: "{{ client_secrets.zitadel_domain }}"
when: client_secrets.zitadel_domain is defined when: client_secrets.zitadel_domain is defined
- name: Set client domain from secrets
set_fact:
client_domain: "{{ client_secrets.client_domain }}"
when: client_secrets.client_domain is defined
roles: roles:
- role: zitadel - role: zitadel
- role: nextcloud
post_tasks: post_tasks:
- name: Display deployment summary - name: Display deployment summary
@ -36,8 +42,11 @@
Deployment complete for client: {{ client_name }} Deployment complete for client: {{ client_name }}
Zitadel: https://{{ zitadel_domain }} Zitadel: https://{{ zitadel_domain }}
Nextcloud: https://nextcloud.{{ client_domain }}
Next steps: Next steps:
1. Login to Zitadel with the admin credentials 1. Login to Zitadel with the admin credentials
2. Change the admin password 2. Create OIDC application in Zitadel for Nextcloud
3. Configure OIDC applications for Nextcloud (when deployed) - Client name: Nextcloud
- Redirect URI: https://nextcloud.{{ client_domain }}/apps/user_oidc/code
3. Configure OIDC in Nextcloud using the client ID and secret from Zitadel

View file

@ -0,0 +1,36 @@
---
# Default variables for nextcloud role
# Nextcloud version
nextcloud_version: "30" # Latest stable version (uses major version tag)
# Database configuration
nextcloud_db_type: "pgsql"
nextcloud_db_host: "nextcloud-db"
nextcloud_db_port: "5432"
nextcloud_db_name: "nextcloud"
nextcloud_db_user: "nextcloud"
# Admin user configuration
nextcloud_admin_user: "admin"
# Nextcloud domain (will be set from client_domain variable)
nextcloud_domain: "nextcloud.{{ client_domain }}"
# Redis configuration for caching and file locking
nextcloud_redis_host: "nextcloud-redis"
nextcloud_redis_port: "6379"
# OIDC configuration
nextcloud_oidc_enabled: true
nextcloud_oidc_provider_url: "https://{{ zitadel_domain }}"
nextcloud_oidc_client_id: "" # Will be set after creating app in Zitadel
nextcloud_oidc_client_secret: "" # Will be set after creating app in Zitadel
# Trusted domains (for Nextcloud config)
nextcloud_trusted_domains:
- "{{ nextcloud_domain }}"
# PHP memory limit
nextcloud_php_memory_limit: "512M"
nextcloud_php_upload_limit: "16G"

View file

@ -0,0 +1,7 @@
---
# Handlers for Nextcloud role
- name: Restart Nextcloud
community.docker.docker_compose_v2:
project_src: /opt/nextcloud
state: restarted

View file

@ -0,0 +1,29 @@
---
# Docker deployment tasks for Nextcloud
- name: Create Nextcloud directory
file:
path: /opt/nextcloud
state: directory
mode: '0755'
- name: Deploy Nextcloud Docker Compose file
template:
src: docker-compose.nextcloud.yml.j2
dest: /opt/nextcloud/docker-compose.yml
mode: '0600'
notify: Restart Nextcloud
- name: Start Nextcloud services
community.docker.docker_compose_v2:
project_src: /opt/nextcloud
state: present
register: nextcloud_deploy
- name: Wait for Nextcloud to be ready
wait_for:
host: localhost
port: 80
delay: 10
timeout: 120
when: nextcloud_deploy.changed

View file

@ -0,0 +1,43 @@
---
# Automated Nextcloud installation tasks using occ commands
- name: Wait for Nextcloud container to be healthy
shell: docker exec -u www-data nextcloud php -v
register: nextcloud_health
retries: 30
delay: 10
until: nextcloud_health.rc == 0
changed_when: false
- name: Wait for Nextcloud auto-installation to complete
shell: "docker exec -u www-data nextcloud php occ status 2>&1 | grep -q 'installed: true'"
register: nextcloud_status
retries: 60
delay: 5
until: nextcloud_status.rc == 0
changed_when: false
- name: Configure trusted domains
shell: |
docker exec -u www-data nextcloud php occ config:system:set trusted_domains 0 --value="{{ nextcloud_domain }}"
- name: Configure overwrite settings for reverse proxy
shell: |
docker exec -u www-data nextcloud php occ config:system:set overwriteprotocol --value="https"
docker exec -u www-data nextcloud php occ config:system:set overwritehost --value="{{ nextcloud_domain }}"
docker exec -u www-data nextcloud php occ config:system:set overwrite.cli.url --value="https://{{ nextcloud_domain }}"
- name: Configure Redis for caching
shell: |
docker exec -u www-data nextcloud php occ config:system:set redis host --value="{{ nextcloud_redis_host }}"
docker exec -u www-data nextcloud php occ config:system:set redis port --value="{{ nextcloud_redis_port }}"
docker exec -u www-data nextcloud php occ config:system:set memcache.local --value="\OC\Memcache\Redis"
docker exec -u www-data nextcloud php occ config:system:set memcache.locking --value="\OC\Memcache\Redis"
- name: Set default phone region
shell: |
docker exec -u www-data nextcloud php occ config:system:set default_phone_region --value="NL"
- name: Run background jobs via cron
shell: |
docker exec -u www-data nextcloud php occ background:cron

View file

@ -0,0 +1,21 @@
---
# Main tasks for Nextcloud deployment
- name: Include Docker deployment tasks
include_tasks: docker.yml
tags:
- nextcloud
- docker
- name: Include installation tasks
include_tasks: install.yml
tags:
- nextcloud
- install
- name: Include OIDC configuration tasks
include_tasks: oidc.yml
when: nextcloud_oidc_enabled | default(true)
tags:
- nextcloud
- oidc

View file

@ -0,0 +1,36 @@
---
# OIDC/SSO integration tasks for Nextcloud with Zitadel
- name: Check if user_oidc app is installed
shell: docker exec -u www-data nextcloud php occ app:list --output=json
register: nextcloud_apps
changed_when: false
- name: Parse installed apps
set_fact:
user_oidc_installed: "{{ 'user_oidc' in (nextcloud_apps.stdout | from_json).enabled }}"
- name: Install user_oidc app
shell: docker exec -u www-data nextcloud php occ app:install user_oidc
when: not user_oidc_installed
register: oidc_install
changed_when: "'installed' in oidc_install.stdout"
- name: Enable user_oidc app
shell: docker exec -u www-data nextcloud php occ app:enable user_oidc
when: not user_oidc_installed
# Note: OIDC provider configuration requires the Zitadel application to be created first
# This will be configured manually or via Zitadel API in a follow-up task
- name: Display OIDC configuration instructions
debug:
msg: |
To complete OIDC setup:
1. Create an OIDC application in Zitadel console at https://{{ zitadel_domain }}
2. Use redirect URI: https://{{ nextcloud_domain }}/apps/user_oidc/code
3. Configure the provider in Nextcloud using:
docker exec -u www-data nextcloud php occ user_oidc:provider:add \
--clientid="<client_id>" \
--clientsecret="<client_secret>" \
--discoveryuri="https://{{ zitadel_domain }}/.well-known/openid-configuration" \
"Zitadel"

View file

@ -0,0 +1,112 @@
services:
# PostgreSQL Database for Nextcloud
nextcloud-db:
image: postgres:16-alpine
container_name: nextcloud-db
restart: unless-stopped
volumes:
- nextcloud-db-data:/var/lib/postgresql/data
environment:
POSTGRES_DB: {{ nextcloud_db_name }}
POSTGRES_USER: {{ nextcloud_db_user }}
POSTGRES_PASSWORD: {{ client_secrets.nextcloud_db_password }}
# Grant full privileges to the user
POSTGRES_INITDB_ARGS: "--auth-host=scram-sha-256"
networks:
- nextcloud-internal
# Redis for caching and file locking
nextcloud-redis:
image: redis:7-alpine
container_name: nextcloud-redis
restart: unless-stopped
command: redis-server --save 60 1 --loglevel warning
volumes:
- nextcloud-redis-data:/data
networks:
- nextcloud-internal
# Nextcloud application
nextcloud:
image: nextcloud:{{ nextcloud_version }}
container_name: nextcloud
restart: unless-stopped
depends_on:
- nextcloud-db
- nextcloud-redis
volumes:
- nextcloud-data:/var/www/html
environment:
# Database configuration
POSTGRES_HOST: {{ nextcloud_db_host }}
POSTGRES_DB: {{ nextcloud_db_name }}
POSTGRES_USER: {{ nextcloud_db_user }}
POSTGRES_PASSWORD: {{ client_secrets.nextcloud_db_password }}
# Redis configuration
REDIS_HOST: {{ nextcloud_redis_host }}
REDIS_HOST_PORT: {{ nextcloud_redis_port }}
# Nextcloud configuration
NEXTCLOUD_ADMIN_USER: {{ nextcloud_admin_user }}
NEXTCLOUD_ADMIN_PASSWORD: {{ client_secrets.nextcloud_admin_password }}
NEXTCLOUD_TRUSTED_DOMAINS: {{ nextcloud_domain }}
OVERWRITEPROTOCOL: https
OVERWRITEHOST: {{ nextcloud_domain }}
OVERWRITECLIURL: https://{{ nextcloud_domain }}
# PHP configuration
PHP_MEMORY_LIMIT: {{ nextcloud_php_memory_limit }}
PHP_UPLOAD_LIMIT: {{ nextcloud_php_upload_limit }}
# SMTP configuration (optional, can be configured via OIDC later)
# SMTP_HOST:
# SMTP_SECURE:
# SMTP_PORT:
# SMTP_NAME:
# SMTP_PASSWORD:
# MAIL_FROM_ADDRESS:
# MAIL_DOMAIN:
networks:
- traefik
- nextcloud-internal
labels:
- "traefik.enable=true"
- "traefik.docker.network=traefik"
# HTTP Router
- "traefik.http.routers.nextcloud.rule=Host(`{{ nextcloud_domain }}`)"
- "traefik.http.routers.nextcloud.entrypoints=websecure"
- "traefik.http.routers.nextcloud.tls=true"
- "traefik.http.routers.nextcloud.tls.certresolver=letsencrypt"
# Middleware for Nextcloud
- "traefik.http.routers.nextcloud.middlewares=nextcloud-headers,nextcloud-redirectregex"
# Security headers
- "traefik.http.middlewares.nextcloud-headers.headers.stsSeconds=31536000"
- "traefik.http.middlewares.nextcloud-headers.headers.stsIncludeSubdomains=true"
- "traefik.http.middlewares.nextcloud-headers.headers.stsPreload=true"
# CalDAV/CardDAV redirect
- "traefik.http.middlewares.nextcloud-redirectregex.redirectregex.permanent=true"
- "traefik.http.middlewares.nextcloud-redirectregex.redirectregex.regex=https://(.*)/.well-known/(card|cal)dav"
- "traefik.http.middlewares.nextcloud-redirectregex.redirectregex.replacement=https://$$1/remote.php/dav/"
# Service
- "traefik.http.services.nextcloud.loadbalancer.server.port=80"
networks:
traefik:
external: true
nextcloud-internal:
name: nextcloud-internal
driver: bridge
volumes:
nextcloud-db-data:
name: nextcloud-db-data
nextcloud-redis-data:
name: nextcloud-redis-data
nextcloud-data:
name: nextcloud-data

View file

@ -14,6 +14,18 @@ http:
middlewares: middlewares:
- zitadel-headers - zitadel-headers
# Nextcloud file sync/share
nextcloud:
rule: "Host(`nextcloud.test.vrije.cloud`)"
service: nextcloud
entryPoints:
- websecure
tls:
certResolver: letsencrypt
middlewares:
- nextcloud-headers
- nextcloud-redirectregex
services: services:
# Zitadel service # Zitadel service
zitadel: zitadel:
@ -21,6 +33,12 @@ http:
servers: servers:
- url: "h2c://zitadel:8080" - url: "h2c://zitadel:8080"
# Nextcloud service
nextcloud:
loadBalancer:
servers:
- url: "http://nextcloud:80"
middlewares: middlewares:
# Zitadel-specific headers # Zitadel-specific headers
zitadel-headers: zitadel-headers:
@ -29,6 +47,20 @@ http:
stsIncludeSubdomains: true stsIncludeSubdomains: true
stsPreload: true stsPreload: true
# Nextcloud-specific headers
nextcloud-headers:
headers:
stsSeconds: 31536000
stsIncludeSubdomains: true
stsPreload: true
# CalDAV/CardDAV redirect for Nextcloud
nextcloud-redirectregex:
redirectRegex:
permanent: true
regex: "https://(.*)/.well-known/(card|cal)dav"
replacement: "https://$1/remote.php/dav/"
# Security headers # Security headers
security-headers: security-headers:
headers: headers: