Your own private cloud — running on your PC, accessible from anywhere in the world, secured with HTTPS. No monthly fees, no third-party storing your files, no port forwarding required.
Stack: Nextcloud · MariaDB · Cloudflare Tunnel · Docker Compose
Works on: Linux, Windows (WSL2)
Bypasses: Jio/BSNL CGNAT — no public IP needed
Your phone (anywhere)
│ HTTPS
▼
Cloudflare Edge ◄──── your domain (e.g. khankloud.in)
│ Tunnel (outbound, no open ports)
▼
cloudflared container
│ http://app:80 (Docker internal network)
▼
Nextcloud container ◄──► MariaDB container
│
▼
Your disk partition (100 GB)
The key insight: instead of opening ports on your router (which doesn't work with CGNAT), your server reaches out to Cloudflare and holds a persistent tunnel. Traffic flows back through that tunnel. No port forwarding, no public IP needed.
- OS: Ubuntu 22.04+ / Windows 11 with WSL2 (Ubuntu)
- Docker: Docker Engine (Linux) or Docker Desktop with WSL2 backend (Windows)
- Domain: A domain you own with nameservers pointed to Cloudflare (free Cloudflare account)
- Disk: A partition or drive to store your data — mounted at a path you control
- Cloudflare Tunnel: Created via Cloudflare Zero Trust dashboard (free)
git clone https://github.com/akhan172/selfhosted-cloud.git
cd selfhosted-cloudLinux:
sudo mkdir -p /mnt/ncdata
# mount your partition here — see Phase 1 of the full guideWindows (WSL2) — easiest:
mkdir -p /mnt/d/nextcloud-data # use your actual drive letterEdit docker-compose.yml and replace:
| Placeholder | Replace with |
|---|---|
your_root_password |
Strong MariaDB root password |
your_db_password |
Strong MariaDB user password |
your_admin_password |
Your Nextcloud admin password |
yourdomain.com |
Your actual domain (e.g. khankloud.in) |
/mnt/d/nextcloud-data |
Your actual data directory path |
YOUR_CLOUDFLARE_TUNNEL_TOKEN |
Token from Cloudflare Zero Trust tunnel |
- Go to Cloudflare Zero Trust → Networks → Tunnels
- Create a tunnel → name it anything → copy the token (starts with
eyJ...) - Paste it in
docker-compose.yml
docker compose up -d
docker compose ps # all 3 should show "running"In Cloudflare Zero Trust → Tunnels → your tunnel → Public Hostnames → Add:
| Field | Value |
|---|---|
| Subdomain | (leave blank) |
| Domain | yourdomain.com |
| Type | HTTP |
| URL | app:80 |
Run once after first startup:
docker compose exec app php occ config:system:set trusted_domains 0 --value='localhost'
docker compose exec app php occ config:system:set trusted_domains 1 --value='yourdomain.com'
docker compose exec app php occ config:system:set trusted_proxies 0 --value='172.20.0.0/16'
docker compose exec app php occ config:system:set overwriteprotocol --value='https'
docker compose exec app php occ config:system:set overwritehost --value='yourdomain.com'| From | URL |
|---|---|
| Home (laptop) | http://localhost:8080 |
| Anywhere (mobile data, other networks) | https://yourdomain.com |
Install the Nextcloud app on your phone, enter your domain, and log in. Enable auto photo backup in the app settings.
- Change all default passwords in
docker-compose.ymlbefore first run - Enable 2FA: Profile → Settings → Security → Two-Factor Authentication (use Google Authenticator or Authy)
- Nextcloud admin panel → Security → run the security scan
Update all containers monthly:
docker compose pull
docker compose down
docker compose up -d502 Bad Gateway on mobile data
Run docker ps and check for duplicate cloudflared containers from a previous session. Stop and remove the old one — it won't be on the correct Docker network.
"Access through untrusted domain" error
Re-run the php occ config:system:set trusted_domains commands from Step 7.
localhost:8080 not reachable
Check that the ports: - "8080:80" line is present under the app service in your compose file.
Cloudflare logs show lookup app: no such host
All three services must be on the same Docker network. Verify all have networks: - cloud and run docker compose down --remove-orphans && docker compose up -d.