A self-hosted Nextcloud setup that runs entirely inside a private Tailscale network — no public internet exposure. All credentials are managed through environment variables, nothing is hardcoded.
This repo ships two compose files:
| File | What it does |
|---|---|
docker-compose.yml |
Spins up one Nextcloud instance |
docker-compose.multi.yml |
Spins up multiple isolated Nextcloud instances on the same server |
Use docker-compose.yml when:
- You need one cloud storage for one person or one team
- Simple setup, one URL, one admin panel
Use docker-compose.multi.yml when:
- You have multiple organizations or departments on the same server
- Each group needs completely separate storage — they cannot see each other's files
- You want one server to serve many tenants, each with their own login, their own data, their own admin
Each tenant in the multi setup is a fully independent Nextcloud — separate database, separate filesystem, separate port. Adding more tenants is just copying a block in the compose file and adding the corresponding variables to
.env.
| Requirement | Details |
|---|---|
| OS | Ubuntu Server 22.04+ |
| Runtime | Docker + Docker Compose |
| Network | Tailscale installed and connected |
| Storage | NAS or any mounted path for data persistence |
Install Docker:
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER && newgrp dockerserver
└── nextcloud → :8080 → /your/data/path/
1. Clone the repo:
git clone https://github.com/AssachanDev/nextcloud-docker.git
cd nextcloud-docker2. Configure environment:
cp .env.example .env
nano .envFill in the single-instance section:
PORT=8080
ADMIN_USER=admin
ADMIN_PASSWORD=your_strong_password
MYSQL_ROOT_PASSWORD=your_strong_password
MYSQL_PASSWORD=your_strong_password
DATA_PATH=/path/to/your/data3. Create data directory:
sudo mkdir -p /path/to/your/data
sudo chown -R 33:33 /path/to/your/data4. Start:
docker compose up -d
docker compose ps5. Add trusted domain:
docker exec -u 33 -it nextcloud php occ \
config:system:set trusted_domains 1 --value=<tailscale-ip>6. Access: http://<tailscale-ip>:8080
Each tenant is a completely independent Nextcloud instance. They share the same server but nothing else — not the database, not the filesystem, not the admin panel.
server
├── tenant-a → :9090 → /your/data/path/company-a/
├── tenant-b → :9091 → /your/data/path/company-b/
└── tenant-c → :9092 → /your/data/path/company-c/ ← just copy the block
1. Configure environment:
Fill in the multi-tenant section in .env:
DATA_PATH=/path/to/your/data
TENANT_A_NAME=company-a
TENANT_A_PORT=9090
TENANT_A_ADMIN_PASSWORD=your_strong_password
TENANT_A_MYSQL_ROOT_PASSWORD=your_strong_password
TENANT_A_MYSQL_PASSWORD=your_strong_password
TENANT_B_NAME=company-b
TENANT_B_PORT=9091
TENANT_B_ADMIN_PASSWORD=your_strong_password
TENANT_B_MYSQL_ROOT_PASSWORD=your_strong_password
TENANT_B_MYSQL_PASSWORD=your_strong_passwordTenant names must be lowercase with no spaces — they are used as database names and Docker container names.
2. Create data directories:
sudo mkdir -p $DATA_PATH/company-a
sudo mkdir -p $DATA_PATH/company-b
sudo chown -R 33:33 $DATA_PATH/3. Start:
docker compose -f docker-compose.multi.yml up -d
docker compose -f docker-compose.multi.yml ps4. Add trusted domain for each tenant:
docker exec -u 33 -it company-a-nextcloud php occ \
config:system:set trusted_domains 1 --value=<tailscale-ip>
docker exec -u 33 -it company-b-nextcloud php occ \
config:system:set trusted_domains 1 --value=<tailscale-ip>5. Access:
| Tenant | URL |
|---|---|
| company-a | http://<tailscale-ip>:9090 |
| company-b | http://<tailscale-ip>:9091 |
To add a third (or fourth, or fifth) tenant:
- Copy a tenant block in
docker-compose.multi.ymland renametenant-b→tenant-c - Add the corresponding variables to
.env(TENANT_C_NAME,TENANT_C_PORT, etc.) - Create the data directory and fix ownership
- Run
docker compose -f docker-compose.multi.yml up -dagain - Set the trusted domain for the new container
Each instance has its own admin panel. Login with admin and the password you set in .env.
Recommended group structure:
| Group | Access |
|---|---|
admin |
Full access — sees all files across all users |
employee |
Sees only their own folder and files explicitly shared with them |
To create groups and users:
- Login as admin → Settings → Users
- Add groups from the left sidebar
- Create user accounts and assign them to a group
nextcloud-docker/
├── docker-compose.yml # single instance
├── docker-compose.multi.yml # multi-tenant
├── .env # your secrets — not committed
├── .env.example # template — safe to commit
├── .gitignore
└── README.md
| Action | Single instance | Multi-tenant |
|---|---|---|
| Start | docker compose up -d |
docker compose -f docker-compose.multi.yml up -d |
| Stop | docker compose down |
docker compose -f docker-compose.multi.yml down |
| Logs | docker compose logs -f nextcloud |
docker compose -f docker-compose.multi.yml logs -f tenant-a |
| Update | docker compose pull && docker compose up -d |
same with -f docker-compose.multi.yml |
| Restart | docker compose restart |
docker compose -f docker-compose.multi.yml restart |
Enable auto-start on reboot:
sudo systemctl enable docker
# Containers use restart: always — they come back up automaticallyTip
Generate strong passwords with: openssl rand -base64 16
Warning
.env is git-ignored. Never commit real credentials.
- Nextcloud is only reachable inside the Tailscale network — never exposed to the public internet
- In multi-tenant mode, tenants are isolated at both the database and filesystem level — no data can cross between them
- Data persists across container restarts and image updates as long as
DATA_PATHis intact - Mobile access: install the Tailscale app first, then connect via the Nextcloud client