How to Deploy Nextcloud – Private Cloud Storage

Nextcloud is an open-source productivity platform that combines file storage with a suite of collaboration tools, including calendars, contacts, and video conferencing. It is designed for privacy and security, giving you full control over your data while offering features similar to those found in Microsoft 365 or Google Workspace.
This article demonstrates how to deploy Nextcloud on Ubuntu 24.04 using Docker Compose.
Prerequisites
Before you begin:
- Access an Ubuntu 24.04 server as a non-root user with sudo privileges.
- Install Docker and Docker Compose.
- Configure a domain name, such as
nextcloud.example.com, pointing to your server’s public IP address.
Set Up the Directory Structure and Environment Variables
Nextcloud requires persistent storage for user data and the database, as well as environment variables to configure the installation. This section sets up the necessary directories and configuration file.
Create the project folders.
console$ mkdir -p ~/nextcloud/{html,mysql,redis,letsencrypt}
html- Stores the main Nextcloud application code and user data.mysql- Persistent storage for the MariaDB database.redis- Stores Redis cache snapshots for persistence between restarts.letsencrypt- Stores Traefik ACME certificates.
Navigate to the root Nextcloud directory.
console$ cd ~/nextcloud
Create an
.envfile to store your configuration.console$ nano .env
Add the following values:
iniNEXTCLOUD_DOMAIN=nextcloud.example.com NEXTCLOUD_ADMIN_USER=admin NEXTCLOUD_ADMIN_PASSWORD=STRONG_ADMIN_PASSWORD MYSQL_DATABASE=nextcloud MYSQL_USER=nextcloud MYSQL_PASSWORD=STRONG_DB_PASSWORD MYSQL_ROOT_PASSWORD=STRONG_ROOT_PASSWORD LETSENCRYPT_EMAIL=admin@example.com
Replace the placeholders with your own values:
nextcloud.example.com– Your actual domain name.STRONG_ADMIN_PASSWORD– The password you will use to log in to the Nextcloud web interface.STRONG_DB_PASSWORD– Secure password for the Nextcloud database user.admin@example.com– Your email for TLS certificate notifications.
Save and close the file.
Deploy with Docker Compose
This section deploys the full Nextcloud stack behind Traefik. The configuration includes services for the application, database, and cache.
Add your user account to the docker user group.
console$ sudo usermod -aG docker $USER
Apply the new group membership.
console$ newgrp docker
Create the Docker Compose manifest file.
console$ nano docker-compose.yml
Add the following contents:
yamlservices: traefik: image: traefik:latest container_name: traefik restart: unless-stopped environment: DOCKER_API_VERSION: "1.44" command: - "--providers.docker=true" - "--providers.docker.exposedbydefault=false" - "--entrypoints.web.address=:80" - "--entrypoints.websecure.address=:443" - "--entrypoints.web.http.redirections.entrypoint.to=websecure" - "--entrypoints.web.http.redirections.entrypoint.scheme=https" - "--certificatesresolvers.le.acme.httpchallenge=true" - "--certificatesresolvers.le.acme.httpchallenge.entrypoint=web" - "--certificatesresolvers.le.acme.email=${LETSENCRYPT_EMAIL}" - "--certificatesresolvers.le.acme.storage=/letsencrypt/acme.json" ports: - "80:80" - "443:443" volumes: - /var/run/docker.sock:/var/run/docker.sock:ro - ./letsencrypt:/letsencrypt mariadb: image: mariadb:10.11 container_name: nextcloud_db restart: unless-stopped command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW environment: MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} MYSQL_PASSWORD: ${MYSQL_PASSWORD} MYSQL_DATABASE: ${MYSQL_DATABASE} MYSQL_USER: ${MYSQL_USER} volumes: - ./mysql:/var/lib/mysql healthcheck: test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"] interval: 10s timeout: 5s retries: 3 redis: image: redis:latest container_name: nextcloud_redis restart: unless-stopped volumes: - ./redis:/data healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 5s retries: 3 nextcloud: image: nextcloud:latest container_name: nextcloud_app restart: unless-stopped depends_on: mariadb: condition: service_healthy redis: condition: service_healthy environment: NEXTCLOUD_ADMIN_USER: ${NEXTCLOUD_ADMIN_USER} NEXTCLOUD_ADMIN_PASSWORD: ${NEXTCLOUD_ADMIN_PASSWORD} NEXTCLOUD_TRUSTED_DOMAINS: ${NEXTCLOUD_DOMAIN} MYSQL_HOST: mariadb MYSQL_DATABASE: ${MYSQL_DATABASE} MYSQL_USER: ${MYSQL_USER} MYSQL_PASSWORD: ${MYSQL_PASSWORD} REDIS_HOST: redis volumes: - ./html:/var/www/html labels: - "traefik.enable=true" - "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=le" - "traefik.http.services.nextcloud.loadbalancer.server.port=80" - "traefik.http.middlewares.nextcloud-dav.redirectregex.regex=^/.well-known/(card|cal)dav" - "traefik.http.middlewares.nextcloud-dav.redirectregex.replacement=/remote.php/dav/" - "traefik.http.routers.nextcloud.middlewares=nextcloud-dav"
Save and close the file.
This Docker Compose file deploys Nextcloud behind Traefik, using MariaDB for structured data and Redis for caching. Each service has a specific purpose:
nextcloud service
- Runs the official
nextcloudimage. - Connects to the
mariadbservice for the database andredisfor transactional file locking and caching. - Automatically runs the installation wizard using the
NEXTCLOUD_ADMIN_USERandNEXTCLOUD_ADMIN_PASSWORDvariables provided. - Stores all application data and uploaded files in the
./htmldirectory (mounted to/var/www/html). - Exposes port 80 internally, which Traefik routes to via labels.
- Includes middleware labels (
nextcloud-dav) to handle CalDAV and CardDAV redirects, ensuring calendar and contact syncing works correctly.
mariadb service
- Runs MariaDB 10.11, configured specifically for Nextcloud requirements (using
READ-COMMITTEDtransaction isolation). - Initializes the database with the credentials from your
.envfile. - Stores database files in the
./mysqldirectory. - Includes a health check to ensure it is fully ready before the Nextcloud container attempts to connect.
redis service
- Runs a Redis server for in-memory caching and distributed file locking. Redis significantly improves Nextcloud's performance and prevents data corruption when multiple users edit files simultaneously.
- Stores cache persistence data in the
./redisdirectory.
traefik service
- Listens on ports 80 and 443 to handle all incoming traffic.
- Automatically requests and renews TLS certificates from Let’s Encrypt for your domain name.
- Redirects all insecure HTTP traffic to HTTPS.
- Runs the official
Start all services in detached mode.
console$ docker compose up -d
Check the container status.
console$ docker compose ps
For more information on managing a Docker Compose stack, see the How To Use Docker Compose article.Note
Access and Configure Nextcloud
By default, Nextcloud restricts access to localhost to prevent header poisoning attacks. To access the site via your domain name, you must add it to the trusted list.
Configure your domain as a trusted domain using the command below. Replace
nextcloud.example.comwith your actual domain name.console$ docker compose exec --user www-data nextcloud php occ config:system:set trusted_domains 1 --value=nextcloud.example.com
--user www-data: Runs the command as the web server user.nextcloud: Target service name defined in the docker-compose file.occ: Nextcloud's command-line administration tool.
Run the following command to enable HTTPS for all generated links using Nextcloud, which prevents the Service from getting stuck in a login loop.
console$ docker compose exec --user www-data nextcloud php occ config:system:set overwriteprotocol --value=https
Open your browser and visit
https://nextcloud.example.com. Enter theNEXTCLOUD_ADMIN_USERandNEXTCLOUD_ADMIN_PASSWORDyou configured in your.envfile.Upon successful login, the Nextcloud dashboard loads.

Conclusion
You have successfully deployed Nextcloud on Ubuntu 24.04 using Docker Compose. Your deployment is now secure and accessible over HTTPS. For more details on managing your instance, visit the Nextcloud Administration Manual.