How to Self-Host Nextcloud and Access It from Anywhere (No Port Forwarding)
Nextcloud is the most complete self-hosted alternative to Google Drive, Dropbox, and Google Photos. This guide covers two installation paths: Docker Compose on any Linux machine, and a dedicated Raspberry Pi setup using the official NextcloudPi script. Both end with a Localtonet tunnel that gives your Nextcloud a public HTTPS URL accessible from any device, anywhere in the world, without port forwarding, a static IP, or a VPS.
What Is Nextcloud?
Nextcloud is an open-source, self-hosted collaboration platform. At its core it is a file sync and share server, but the app ecosystem makes it much more: calendar and contacts sync, photo gallery, office document editing (via Collabora or OnlyOffice), video calls, password manager, and more. Every piece of data stays on hardware you own.
Self-Hosting vs Google Drive / Dropbox
- Your files never leave your hardware
- No per-user monthly fee, no storage cap beyond your disk
- No scanning, no advertising, no data sharing
- GDPR and HIPAA compliance under your control
- Works on your local network even without internet
Responsibilities
- You manage updates and security patches
- You are responsible for backups
- Uptime depends on your hardware and power
- Initial setup takes more effort than signing up for a service
Requirements
| Component | Minimum | Recommended |
|---|---|---|
| CPU | 64-bit, 2 cores | 4 cores (Raspberry Pi 4 / 5 or x86_64 server) |
| RAM | 2 GB | 4 GB+ (Redis + MariaDB + Nextcloud all run simultaneously) |
| Storage | 16 GB (OS + app) | SSD for OS/DB, large HDD/external drive for user data |
| OS | Debian 12, Ubuntu 22.04+, Raspberry Pi OS | Ubuntu 24.04 LTS or Raspberry Pi OS Bookworm (64-bit) |
| Docker | Docker 24+, Docker Compose v2 | Latest stable Docker Engine |
| Network | Any internet connection (including CGNAT) | Stable broadband. Localtonet handles CGNAT automatically. |
Many ISPs in 2026 use CGNAT (Carrier Grade NAT), which means you do not have a real public IP address at your home. Port forwarding on your router does nothing. Localtonet's outbound tunnel solves this completely because it does not require any inbound port to be open.
Installation Path 1: Docker Compose on Linux
This is the recommended method for any Linux machine (Ubuntu, Debian, or any x86_64 server). It installs Nextcloud together with MariaDB and Redis in isolated containers, making upgrades easy and the host system clean.
Step 1: Install Docker
# Install Docker (one-line official script):
curl -fsSL https://get.docker.com | sudo sh
# Add your user to the docker group (no sudo needed):
sudo usermod -aG docker $USER
# Log out and back in, then verify:
docker --version
docker compose version
Expected output:
Docker version 27.x.x, build xxxxxxx
Docker Compose version v2.x.x
Step 2: Create the project directory and .env file
mkdir ~/nextcloud && cd ~/nextcloud
Create a .env file with your passwords. Never commit this file to Git:
cat > .env << 'EOF'
MYSQL_ROOT_PASSWORD=change_this_root_password
MYSQL_PASSWORD=change_this_db_password
MYSQL_DATABASE=nextcloud
MYSQL_USER=nextcloud
NEXTCLOUD_ADMIN_USER=admin
NEXTCLOUD_ADMIN_PASSWORD=change_this_admin_password
NEXTCLOUD_TRUSTED_DOMAINS=localhost
EOF
Step 3: Create docker-compose.yml
cat > docker-compose.yml << 'EOF'
version: "3.8"
services:
db:
image: mariadb:11
container_name: nextcloud-db
restart: unless-stopped
command: --transaction-isolation=READ-COMMITTED --log-bin=binlog --binlog-format=ROW
volumes:
- db_data:/var/lib/mysql
env_file: .env
environment:
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
- MYSQL_DATABASE=${MYSQL_DATABASE}
- MYSQL_USER=${MYSQL_USER}
- MYSQL_PASSWORD=${MYSQL_PASSWORD}
redis:
image: redis:alpine
container_name: nextcloud-redis
restart: unless-stopped
app:
image: nextcloud:stable-apache
container_name: nextcloud-app
restart: unless-stopped
ports:
- "8080:80"
depends_on:
- db
- redis
volumes:
- nextcloud_html:/var/www/html
- ./data:/var/www/html/data
env_file: .env
environment:
- MYSQL_HOST=db
- MYSQL_DATABASE=${MYSQL_DATABASE}
- MYSQL_USER=${MYSQL_USER}
- MYSQL_PASSWORD=${MYSQL_PASSWORD}
- REDIS_HOST=redis
- NEXTCLOUD_ADMIN_USER=${NEXTCLOUD_ADMIN_USER}
- NEXTCLOUD_ADMIN_PASSWORD=${NEXTCLOUD_ADMIN_PASSWORD}
- NEXTCLOUD_TRUSTED_DOMAINS=${NEXTCLOUD_TRUSTED_DOMAINS}
- APACHE_DISABLE_REWRITE_IP=1
- TRUSTED_PROXIES=172.16.0.0/12 192.168.0.0/16 10.0.0.0/8
volumes:
db_data:
nextcloud_html:
EOF
Step 4: Start Nextcloud
docker compose up -d
The first run pulls images and runs the Nextcloud installer automatically. Wait about 2 minutes, then open http://localhost:8080 in your browser. You should see the Nextcloud login screen.
# Check that all three containers are running:
docker compose ps
Expected output:
NAME IMAGE STATUS
nextcloud-app nextcloud:stable-apache Up 2 minutes
nextcloud-db mariadb:11 Up 2 minutes
nextcloud-redis redis:alpine Up 2 minutes
Step 5: Set up the background cron job
Nextcloud requires periodic background tasks (file scanning, notifications, cleanup). The default AJAX-based cron only runs when someone is logged in. Switch to a proper system cron:
# Add a cron job that runs every 5 minutes:
(crontab -l 2>/dev/null; echo "*/5 * * * * docker exec --user www-data nextcloud-app php -f /var/www/html/cron.php") | crontab -
# Then set the cron type in Nextcloud to "cron":
docker exec --user www-data nextcloud-app php occ background:cron
Expected output:
Set mode for background jobs to 'cron'
Installation Path 2: Raspberry Pi (NextcloudPi)
The Raspberry Pi deserves its own section because it is the most popular self-hosting hardware for Nextcloud. The official NextcloudPi project provides a dedicated OS image and install script that sets everything up automatically, including Apache, PHP, MariaDB, and Redis, optimized for the Pi's constraints.
Raspberry Pi 4 (4 GB RAM) or Raspberry Pi 5 (4 GB or 8 GB RAM) are the minimum for a usable Nextcloud experience. Pi 3 works for personal use with few files. Pi Zero and Pi 1 are too slow. Use a quality microSD card (A2-rated) or, better, a USB-attached SSD for the OS and Nextcloud data. An external USB HDD is fine for bulk storage.
Option A: NextcloudPi OS Image (Recommended for beginners)
Download the NextcloudPi image
Go to github.com/nextcloud/nextcloudpi/releases and download the latest .img.xz file for your Pi model. Use Raspberry Pi Imager to flash it to your SD card or SSD.
Boot and find the Pi's IP address
Connect the Pi to your router via Ethernet and power it on. Find its local IP from your router's DHCP list or run ping nextcloudpi.local from another machine on the same network.
Open the activation wizard
Navigate to https://<pi-ip>:4443 in your browser. Accept the self-signed certificate warning. The NextcloudPi activation wizard guides you through setting the admin password and configuring storage.
Access Nextcloud
After activation, Nextcloud is available at https://<pi-ip> on your local network. The NextcloudPi panel is at https://<pi-ip>:4443.
Option B: Install NextcloudPi on Existing Raspberry Pi OS
If your Pi already runs Raspberry Pi OS Bookworm (64-bit), install NextcloudPi on top with the official script:
# Update the system first:
sudo apt update && sudo apt upgrade -y
# Run the NextcloudPi installer:
curl -sSL https://raw.githubusercontent.com/nextcloud/nextcloudpi/master/install.sh | sudo bash
The installer takes 5-15 minutes depending on your Pi model and internet speed. When complete it prints:
NextcloudPi instance already running on this machine.
First run wizard
---------------------------------------------------------------
It seems this is your first run of NextcloudPi.
Navigate to https://<your-pi-ip>:4443
to complete the initial configuration.
---------------------------------------------------------------
Option C: Docker Compose on Raspberry Pi
If you prefer Docker on your Pi (for easier updates and multi-container management), the same docker-compose.yml from Path 1 works on Raspberry Pi OS 64-bit without changes. The nextcloud:stable-apache image includes an ARM64 build.
# Install Docker on Raspberry Pi OS:
curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker pi
# Log out and back in, then run the same docker-compose.yml from Path 1
Nextcloud's official Docker image requires a 64-bit OS. The default Raspberry Pi OS for older Pi models is 32-bit. Flash the 64-bit version of Raspberry Pi OS from the Raspberry Pi Imager before installing Docker. You can verify with uname -m: it should return aarch64, not armv7l.
Raspberry Pi: Mounting an External Drive for Data
Storing Nextcloud data on the SD card will wear it out quickly. Mount an external USB SSD or HDD:
# Find the drive:
lsblk
# Create a mount point and mount:
sudo mkdir -p /mnt/nextcloud-data
sudo mount /dev/sda1 /mnt/nextcloud-data
# Make mount permanent (add to /etc/fstab):
echo "UUID=$(sudo blkid -s UUID -o value /dev/sda1) /mnt/nextcloud-data ext4 defaults,nofail 0 2" | sudo tee -a /etc/fstab
# Fix permissions for Nextcloud (www-data user):
sudo chown -R www-data:www-data /mnt/nextcloud-data
In Docker Compose on Pi, point the data volume to this path:
volumes:
- /mnt/nextcloud-data:/var/www/html/data
Critical: trusted_proxies Configuration for Tunnel Access
This is the most common point of failure when exposing Nextcloud through any reverse proxy or tunnel. If trusted_proxies is not configured, Nextcloud rejects requests that come through the tunnel and logs errors like "Blocked request from an untrusted proxy" or shows a blank page after login.
Nextcloud must be told which IP addresses are allowed to act as a proxy. When traffic arrives via the Localtonet tunnel, it looks like it comes from the Docker bridge network. Without the trusted_proxies setting, Nextcloud blocks it.
For Docker Compose installs
The TRUSTED_PROXIES environment variable in the docker-compose.yml above already covers all private IP ranges. Verify it is set:
TRUSTED_PROXIES=172.16.0.0/12 192.168.0.0/16 10.0.0.0/8
APACHE_DISABLE_REWRITE_IP=1
After changing, restart the app container:
docker compose restart app
For NextcloudPi (Raspberry Pi) installs
Edit the Nextcloud config file directly:
# On NextcloudPi, config.php is at:
sudo nano /var/www/nextcloud/config/config.php
# Add or update this block inside the $CONFIG array:
'trusted_proxies' =>
array (
0 => '127.0.0.1',
1 => '::1',
2 => '172.16.0.0/12',
3 => '192.168.0.0/16',
4 => '10.0.0.0/8',
),
'overwriteprotocol' => 'https',
Then restart Apache:
sudo systemctl restart apache2
Expose Nextcloud to the Internet with Localtonet
With Nextcloud running locally, the next step is making it accessible from anywhere. Localtonet creates an encrypted outbound tunnel from your machine to a public HTTPS endpoint. No port forwarding, no static IP, and it works behind CGNAT.
Install Localtonet
# Linux (Ubuntu, Debian, Raspberry Pi OS):
curl -fsSL https://localtonet.com/install.sh | sh
# Verify:
localtonet --version
Authenticate with your AuthToken
Register at localtonet.com, go to Dashboard → My Tokens, copy your token, and authenticate:
localtonet --authtoken YOUR_TOKEN_HERE
Create an HTTP tunnel for Nextcloud
Go to localtonet.com/tunnel/http and fill in:
- IP:
127.0.0.1 - Port:
8080(Docker Compose) or80(NextcloudPi) - AuthToken: Select your token
- Custom Subdomain (optional): e.g.
mycloud
Click Create, then Start.
Add the public URL to Nextcloud's trusted domains
The dashboard shows your public URL, e.g. https://mycloud.localto.net. Add it as a trusted domain so Nextcloud accepts requests from it:
# Docker Compose:
docker exec --user www-data nextcloud-app php occ config:system:set trusted_domains 1 --value="mycloud.localto.net"
# NextcloudPi:
sudo -u www-data php /var/www/nextcloud/occ config:system:set trusted_domains 1 --value="mycloud.localto.net"
Expected output:
System config value trusted_domains => 1 set to string mycloud.localto.net
Also set overwritehost and overwriteprotocol
This tells Nextcloud to generate links using your public domain and HTTPS:
# Docker Compose:
docker exec --user www-data nextcloud-app php occ config:system:set overwritehost --value="mycloud.localto.net"
docker exec --user www-data nextcloud-app php occ config:system:set overwriteprotocol --value="https"
# NextcloudPi:
sudo -u www-data php /var/www/nextcloud/occ config:system:set overwritehost --value="mycloud.localto.net"
sudo -u www-data php /var/www/nextcloud/occ config:system:set overwriteprotocol --value="https"
Run Localtonet as a service (always-on)
sudo localtonet --install-service --authtoken YOUR_TOKEN_HERE
sudo localtonet --start-service --authtoken YOUR_TOKEN_HERE
The tunnel now starts automatically on every reboot alongside Nextcloud.
Essential occ Commands for Nextcloud Administration
The occ (ownCloud Console) tool is the Nextcloud command-line administration tool. Many settings that look confusing in the web UI are one-liners via occ. Always run it as the www-data user.
| Task | Docker Compose Command |
|---|---|
| Check overall status | docker exec --user www-data nextcloud-app php occ status |
| List all config values | docker exec --user www-data nextcloud-app php occ config:list |
| Add trusted domain | docker exec --user www-data nextcloud-app php occ config:system:set trusted_domains 2 --value="yourdomain.com" |
| Enable maintenance mode | docker exec --user www-data nextcloud-app php occ maintenance:mode --on |
| Disable maintenance mode | docker exec --user www-data nextcloud-app php occ maintenance:mode --off |
| Run database migrations | docker exec --user www-data nextcloud-app php occ db:add-missing-indices |
| Scan all user files | docker exec --user www-data nextcloud-app php occ files:scan --all |
| List installed apps | docker exec --user www-data nextcloud-app php occ app:list |
| Install an app | docker exec --user www-data nextcloud-app php occ app:install memories |
| Upgrade Nextcloud | docker exec --user www-data nextcloud-app php occ upgrade |
| Create a user | docker exec --user www-data nextcloud-app php occ user:add username |
Backing Up Your Nextcloud
Nextcloud data lives in three places that all must be backed up together: the database, the application config, and the user data files.
Docker Compose Backup Script
#!/bin/bash
# Save as ~/nextcloud/backup.sh and run via cron: 0 2 * * * ~/nextcloud/backup.sh
BACKUP_DIR="/mnt/backup/nextcloud/$(date +%F)"
mkdir -p "$BACKUP_DIR"
cd ~/nextcloud
# 1. Enable maintenance mode:
docker exec --user www-data nextcloud-app php occ maintenance:mode --on
# 2. Dump the database:
docker exec nextcloud-db mysqldump \
-u nextcloud -p"$MYSQL_PASSWORD" nextcloud \
> "$BACKUP_DIR/nextcloud-db.sql"
# 3. Back up config and data:
rsync -a ./data/ "$BACKUP_DIR/data/"
rsync -a $(docker volume inspect nextcloud_nextcloud_html --format '{{.Mountpoint}}'/config) "$BACKUP_DIR/config/"
# 4. Disable maintenance mode:
docker exec --user www-data nextcloud-app php occ maintenance:mode --off
echo "Backup complete: $BACKUP_DIR"
NextcloudPi Backup (Raspberry Pi)
NextcloudPi has a built-in backup tool accessible from the panel at https://<pi-ip>:4443. Go to Config → nc-backup to set a backup path and schedule. Or use the command-line:
# Manual backup via ncp-config:
sudo ncp-config
# Navigate to: Config > nc-backup
# Set the destination path (e.g., /mnt/backup)
# Run backup
# Or directly:
sudo -u www-data php /var/www/nextcloud/occ maintenance:mode --on
sudo rsync -a /var/www/nextcloud/data/ /mnt/backup/nextcloud-data/
sudo mysqldump -u nextcloud -p nextcloud > /mnt/backup/nextcloud-db.sql
sudo -u www-data php /var/www/nextcloud/occ maintenance:mode --off
Updating Nextcloud
Update with Docker Compose
cd ~/nextcloud
# Pull new images:
docker compose pull
# Recreate containers:
docker compose up -d --remove-orphans
# Run the Nextcloud upgrade:
docker exec --user www-data nextcloud-app php occ upgrade
# Fix any missing database indices:
docker exec --user www-data nextcloud-app php occ db:add-missing-indices
# Clear caches:
docker exec --user www-data nextcloud-app php occ maintenance:repair
Do not upgrade from Nextcloud 28 directly to Nextcloud 30. Upgrade one major version at a time (28 → 29 → 30). Check docker exec --user www-data nextcloud-app php occ status before and after each upgrade to confirm the version.
Update with NextcloudPi
# Via the web panel at https://<pi-ip>:4443:
# Navigate to: Updates > nc-update-nextcloud
# Or via command line:
sudo ncp-update
# To update only Nextcloud (not the Pi OS):
sudo -u www-data php /var/www/nextcloud/updater/updater.phar
Troubleshooting
| Problem | Cause | Fix |
|---|---|---|
| "Access through untrusted domain" | The hostname used to access Nextcloud is not in trusted_domains | Run: docker exec --user www-data nextcloud-app php occ config:system:set trusted_domains 1 --value="yourdomain" |
| Blank page or redirect loop after login via tunnel | trusted_proxies not set or overwriteprotocol missing | Add TRUSTED_PROXIES=172.16.0.0/12 192.168.0.0/16 10.0.0.0/8 and APACHE_DISABLE_REWRITE_IP=1 to docker-compose.yml environment. Restart app container. |
| Files not appearing after upload via external drive | Nextcloud file index not updated | Run: docker exec --user www-data nextcloud-app php occ files:scan --all |
| Database errors after upgrade | Missing indices or columns from migration | Run: docker exec --user www-data nextcloud-app php occ db:add-missing-indices and occ maintenance:repair |
| Nextcloud stuck in maintenance mode | Failed upgrade or manual mode left on | Run: docker exec --user www-data nextcloud-app php occ maintenance:mode --off |
| Slow performance on Raspberry Pi | SD card I/O bottleneck or missing Redis cache | Move data to USB SSD. Confirm Redis is running: docker compose ps nextcloud-redis. Add APCu for local cache in config.php. |
| Mobile app cannot connect via public URL | overwriteprotocol or overwritehost not set | Set both values via occ as shown in the tunnel setup section. The mobile app requires HTTPS links generated by Nextcloud to match the domain you are accessing. |
| Nextcloud container keeps restarting | MariaDB not ready when Nextcloud starts | Add depends_on: db to the app service in docker-compose.yml. If already present, check DB logs: docker compose logs db. |
Frequently Asked Questions
Can I self-host Nextcloud without a static IP address?
Yes. A Localtonet tunnel creates an outbound connection from your machine to a public endpoint, so no inbound static IP or open port is required. The tunnel works even behind CGNAT (Carrier Grade NAT), which is common with home ISPs in 2026. Your Nextcloud gets a persistent public HTTPS URL regardless of your IP address.
What is the best Raspberry Pi model for self-hosting Nextcloud?
Raspberry Pi 4 (4 GB RAM) or Raspberry Pi 5 (4 GB or 8 GB RAM) are the minimum for a smooth experience. The Pi 5 is significantly faster and handles concurrent users and the Memories photo app much better. Always boot from a USB SSD rather than a microSD card for much better I/O performance and reliability. Pi Zero and Pi 3 are too slow for general Nextcloud use.
What is trusted_proxies in Nextcloud and why does it matter?
Nextcloud's trusted_proxies setting tells Nextcloud which IP addresses are allowed to act as reverse proxies. When you use a tunnel like Localtonet, traffic arrives with the tunnel's internal IP in the headers. Without trusted_proxies covering that IP range, Nextcloud rejects the request, resulting in a blank page, redirect loop, or "untrusted proxy" error. Setting TRUSTED_PROXIES=172.16.0.0/12 192.168.0.0/16 10.0.0.0/8 in the Docker environment covers all private IP ranges including Docker bridge networks.
How do I access Nextcloud on my phone?
Install the Nextcloud app for iOS or Android. Open it, tap "Add account", and enter your public HTTPS URL (e.g. https://mycloud.localto.net). Log in with your admin or user credentials. File sync, photo backup, calendar, and contacts all work through the app. Make sure overwriteprotocol is set to https and overwritehost matches your public domain, otherwise the app may generate incorrect internal links.
How much storage can I use with self-hosted Nextcloud?
As much as you attach to the machine. There is no artificial storage limit. A common setup is a Raspberry Pi with a 4 TB external USB hard drive for data. For larger setups, Nextcloud supports external storage backends including SMB/NFS network shares, Amazon S3, and other object storage services. The data directory can be changed after installation using the datadirectory setting in config.php.
Is self-hosted Nextcloud secure?
Yes, when configured correctly. Key security steps: use strong admin and user passwords, keep Nextcloud updated, always access via HTTPS (the Localtonet tunnel provides this automatically), enable two-factor authentication for the admin account, and do not expose ports 80 or 443 directly from your router if you use a tunnel. Run Nextcloud's built-in security scan at Settings → Administration → Overview to see any configuration warnings.
Access Your Self-Hosted Nextcloud from Anywhere
Localtonet creates a secure HTTPS tunnel for your Nextcloud instance without port forwarding, a static IP, or a VPS. Works behind CGNAT. Free to start.
Get Started Free →