Coolify Setup: Application Deployment with Self-Hosted PaaS
Coolify installation, Docker and Git integration, application deployment, database management, SSL certificates, environment variables, and self-hosted PaaS alternative to Heroku/Netlify.
Nextcloud installation with Docker, Apache/Nginx configuration, database setup, SSL certificate, file synchronization, app store, and performance optimization.
Nextcloud is an open-source, self-hosted alternative to cloud storage services like Google Drive, Dropbox, and OneDrive. Beyond file storage and synchronization, it offers calendar, contacts, video conferencing, document editing, and much more. This guide covers Nextcloud installation with Docker Compose, Nginx reverse proxy configuration, and performance optimization.
Nextcloud is a collaboration platform that gives you full control over your data:
mkdir -p /opt/nextcloud/{data,db,redis}
cd /opt/nextcloud
version: '3.8'
services:
db:
image: postgres:16-alpine
container_name: nextcloud-db
restart: unless-stopped
environment:
POSTGRES_DB: nextcloud
POSTGRES_USER: nextcloud
POSTGRES_PASSWORD: strong-db-password
volumes:
- ./db:/var/lib/postgresql/data
networks:
- nextcloud-net
redis:
image: redis:7-alpine
container_name: nextcloud-redis
restart: unless-stopped
command: redis-server --requirepass redis-password
volumes:
- ./redis:/data
networks:
- nextcloud-net
app:
image: nextcloud:28-fpm-alpine
container_name: nextcloud-app
restart: unless-stopped
depends_on:
- db
- redis
environment:
POSTGRES_HOST: db
POSTGRES_DB: nextcloud
POSTGRES_USER: nextcloud
POSTGRES_PASSWORD: strong-db-password
NEXTCLOUD_ADMIN_USER: admin
NEXTCLOUD_ADMIN_PASSWORD: admin-password
NEXTCLOUD_TRUSTED_DOMAINS: cloud.example.com
REDIS_HOST: redis
REDIS_HOST_PASSWORD: redis-password
PHP_MEMORY_LIMIT: 512M
PHP_UPLOAD_LIMIT: 10G
volumes:
- ./data:/var/www/html
networks:
- nextcloud-net
nginx:
image: nginx:alpine
container_name: nextcloud-nginx
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./data:/var/www/html:ro
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./ssl:/etc/ssl/nextcloud:ro
depends_on:
- app
networks:
- nextcloud-net
cron:
image: nextcloud:28-fpm-alpine
container_name: nextcloud-cron
restart: unless-stopped
volumes:
- ./data:/var/www/html
entrypoint: /cron.sh
depends_on:
- db
- redis
networks:
- nextcloud-net
networks:
nextcloud-net:
driver: bridge
nano /opt/nextcloud/nginx.conf
worker_processes auto;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
upstream php-handler {
server nextcloud-app:9000;
}
server {
listen 80;
server_name cloud.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name cloud.example.com;
ssl_certificate /etc/ssl/nextcloud/fullchain.pem;
ssl_certificate_key /etc/ssl/nextcloud/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
root /var/www/html;
index index.php index.html;
client_max_body_size 10G;
fastcgi_buffers 64 4K;
add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload" always;
add_header X-Content-Type-Options nosniff always;
add_header X-Frame-Options SAMEORIGIN always;
location = /robots.txt { allow all; log_not_found off; access_log off; }
location ^~ /.well-known {
location = /.well-known/carddav { return 301 /remote.php/dav/; }
location = /.well-known/caldav { return 301 /remote.php/dav/; }
return 301 /index.php$request_uri;
}
location ~ ^\/(?:index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|oc[ms]-provider\/.+)\.php(?:$|\/) {
fastcgi_split_path_info ^(.+?\.php)(\/.*|)$;
set $path_info $fastcgi_path_info;
try_files $fastcgi_script_name =404;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $path_info;
fastcgi_param HTTPS on;
fastcgi_pass php-handler;
fastcgi_intercept_errors on;
fastcgi_request_buffering off;
fastcgi_read_timeout 3600;
}
location ~ \.(?:css|js|svg|gif|png|jpg|ico)$ {
try_files $uri /index.php$request_uri;
expires 6M;
access_log off;
}
location / {
try_files $uri $uri/ /index.php$request_uri;
}
}
}
# Let's Encrypt certificate with Certbot
apt install certbot -y
certbot certonly --standalone -d cloud.example.com
# Copy certificates
mkdir -p /opt/nextcloud/ssl
cp /etc/letsencrypt/live/cloud.example.com/fullchain.pem /opt/nextcloud/ssl/
cp /etc/letsencrypt/live/cloud.example.com/privkey.pem /opt/nextcloud/ssl/
# Auto-renewal cron
echo "0 3 * * * certbot renew --quiet && cp /etc/letsencrypt/live/cloud.example.com/*.pem /opt/nextcloud/ssl/ && docker restart nextcloud-nginx" | crontab -
cd /opt/nextcloud
docker compose up -d
# Follow logs
docker compose logs -f app
# Access after installation:
# https://cloud.example.com
Manage Nextcloud with the occ command-line tool:
# Run occ command
docker exec -u www-data nextcloud-app php occ [command]
# Check system status
docker exec -u www-data nextcloud-app php occ status
# Toggle maintenance mode
docker exec -u www-data nextcloud-app php occ maintenance:mode --on
docker exec -u www-data nextcloud-app php occ maintenance:mode --off
# Add missing indices
docker exec -u www-data nextcloud-app php occ db:add-missing-indices
# Scan files (detect new files)
docker exec -u www-data nextcloud-app php occ files:scan --all
# Clear cache
docker exec -u www-data nextcloud-app php occ cache:clear
# List users
docker exec -u www-data nextcloud-app php occ user:list
# Create new user
docker exec -u www-data nextcloud-app php occ user:add --password-from-env --display-name="Full Name" username
# List available apps
docker exec -u www-data nextcloud-app php occ app:list
# Install apps
docker exec -u www-data nextcloud-app php occ app:install calendar
docker exec -u www-data nextcloud-app php occ app:install contacts
docker exec -u www-data nextcloud-app php occ app:install talk
docker exec -u www-data nextcloud-app php occ app:install onlyoffice
# Enable/disable apps
docker exec -u www-data nextcloud-app php occ app:enable deck
docker exec -u www-data nextcloud-app php occ app:disable activity
Redis is already configured in docker-compose.yml. Add to Nextcloud config:
docker exec -u www-data nextcloud-app php occ config:system:set redis host --value="redis"
docker exec -u www-data nextcloud-app php occ config:system:set redis port --value=6379 --type=integer
docker exec -u www-data nextcloud-app php occ config:system:set redis password --value="redis-password"
docker exec -u www-data nextcloud-app php occ config:system:set memcache.local --value="\\OC\\Memcache\\APCu"
docker exec -u www-data nextcloud-app php occ config:system:set memcache.distributed --value="\\OC\\Memcache\\Redis"
docker exec -u www-data nextcloud-app php occ config:system:set memcache.locking --value="\\OC\\Memcache\\Redis"
Cron is recommended for Nextcloud background tasks:
# The Nextcloud cron container is already running
# Set cron mode
docker exec -u www-data nextcloud-app php occ background:cron
# Increase PHP limits (add to docker-compose.yml environment)
PHP_MEMORY_LIMIT=1G
PHP_UPLOAD_LIMIT=10G
# Add trusted domain
docker exec -u www-data nextcloud-app php occ config:system:set trusted_domains 1 --value="cloud.example.com"
# Brute force protection
docker exec -u www-data nextcloud-app php occ config:system:set auth.bruteforce.protection.enabled --value=true --type=boolean
# Enforce 2FA
docker exec -u www-data nextcloud-app php occ twofactorauth:enforce --on
We recommend using SSD storage for Nextcloud on REXE servers. NVMe SSD performance makes a critical difference for large file transfers.
This error occurs because the domain you're accessing is not in Nextcloud's trusted domain list. Add the domain with: 'docker exec -u www-data nextcloud-app php occ config:system:set trusted_domains 1 --value="cloud.example.com"'. Alternatively, edit /opt/nextcloud/data/config/config.php and add it to the trusted_domains array.
Increase PHP_UPLOAD_LIMIT and PHP_MEMORY_LIMIT values in docker-compose.yml. Also update the client_max_body_size value in Nginx configuration. Set fastcgi_read_timeout to 3600 or higher. Restart containers after making changes.
Make sure Redis caching is active. Enable APCu. Add database indices with 'occ db:add-missing-indices'. If using PostgreSQL (faster than MySQL), run VACUUM ANALYZE. Check that the cron job for preview generation is running regularly.
Update the image version in docker-compose.yml (e.g., nextcloud:28 → nextcloud:29). Run 'docker compose pull && docker compose up -d'. After the update, run 'occ upgrade' and 'occ db:add-missing-indices'. Always take a backup before major version updates.
The Talk app requires a TURN/STUN server. Set up Coturn or use Nextcloud's free STUN server. Configure the STUN/TURN server address in Talk settings. Make sure ports 3478 (UDP/TCP) and 5349 (TLS) are open.
Enable the 'External Storage Support' app in Nextcloud. Go to Settings → Administration → External Storages in the admin panel. You can add storage types like S3, FTP, SFTP, SMB/CIFS, WebDAV. You can define different external storage for each user or group.
Coolify installation, Docker and Git integration, application deployment, database management, SSL certificates, environment variables, and self-hosted PaaS alternative to Heroku/Netlify.
Portainer CE installation, Docker and Kubernetes management, stack deployment, volume/network management, user authorization, webhook and GitOps integration.
Uptime Kuma installation with Docker, HTTP/TCP/DNS/ping monitoring, notification integrations (Telegram, Discord, email), status page creation, and alert configuration.