Nextcloud¶
Nextcloud is the single-domain file and groupware app on my server. Two ways to deploy it: All-in-One (AIO), a managed container set with a web interface, or plain docker compose, which is what I use since I already run compose for everything and just add Collabora or OnlyOffice myself. Because Nextcloud accepts exactly one domain, it has to be paired with Context-Aware DNS so that domain resolves correctly on the LAN, the tailnet, and the public internet.
Option A — All-in-One (AIO)¶
AIO is a container-management layer for Nextcloud's official containers. Launch the master container following the reverse-proxy docs, with these adjustments:
sudo docker run -d \
--init \
--sig-proxy=false \
--name nextcloud-aio-mastercontainer \
--restart always \
--publish 127.0.0.1:8089:8080 \ # bind to localhost, don't expose directly
--env APACHE_PORT=11000 \
--env APACHE_IP_BINDING=0.0.0.0 \
--env APACHE_ADDITIONAL_NETWORK="" \
--env SKIP_DOMAIN_VALIDATION=true \ # domain validation fails for local/tailnet IPs
--volume nextcloud_aio_mastercontainer:/mnt/docker-aio-config \ # must be a docker volume, not a bind mount
--volume /var/run/docker.sock:/var/run/docker.sock:ro \ # AIO needs the sock to create containers
ghcr.io/nextcloud-releases/all-in-one:latest
Access the AIO interface through the reverse proxy (not the raw port — poor practice). Proxy it like any app; note Nextcloud warns HSTS could break the AIO UI in the future, so comment out the HSTS include for that block if it ever misbehaves.
The trade-off: if you run your apps with compose, AIO is annoying — you manage containers through its interface instead of your normal compose workflow.
Option B — plain docker compose (preferred)¶
Standard nextcloud compose with bind mounts driven by .env:
services:
db:
image: mariadb:lts
restart: always
command: --transaction-isolation=READ-COMMITTED
volumes:
- ${DB_DATA_LOCATION}:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD=${DB_ROOT_PW}
- MYSQL_PASSWORD=${DB_PW}
- MYSQL_DATABASE=nextcloud
- MYSQL_USER=${DB_USER}
redis:
image: redis:alpine
restart: always
app:
image: nextcloud
restart: always
ports:
- 12000:80
depends_on: [redis, db]
volumes:
- ${NC_DATA_LOCATION}:/var/www/html
dns:
- 192.168.4.100 # the local DNS server, see Context-Aware DNS
Add Collabora yourself (OnlyOffice optional): collabora/code with DONT_GEN_SSL_CERT=YES and --o:ssl.enable=false --o:ssl.termination=true since Nginx terminates TLS, plus aliasgroup1 for the allowed hosts.
Config¶
config.php→trusted_domainsmust list every way the site is reached:localhost, the server IP, andnextcloud.<domain>.com— otherwise access outside the host's localhost errors out.- One domain only — which is why context-aware DNS is needed for it.
See also¶
- Context-Aware DNS — the piece Nextcloud depends on
- Blog posts: Nextcloud AIO · Nextcloud with compose · Context-aware DNS