diff --git a/services/nextcloud/.env b/services/nextcloud/.env new file mode 100644 index 0000000..0355c41 --- /dev/null +++ b/services/nextcloud/.env @@ -0,0 +1,35 @@ +#version=1.1 +#URL=https://github.com/tailscale-dev/ScaleTail +#COMPOSE_PROJECT_NAME= # Optional: only use when running multiple deployments on the same infrastructure. + +# Service Configuration +SERVICE=nextcloud +IMAGE_URL=nextcloud:apache # Docker image URL from container registry + +# Network Configuration +SERVICEPORT=80 # Port to expose to local network. Uncomment the "ports:" section in compose.yaml to enable. +DNS_SERVER=9.9.9.9 # Preferred DNS server for Tailscale. Uncomment the "dns:" section in compose.yaml to enable. + +# Tailscale Configuration +TS_AUTHKEY= # Auth key from https://tailscale.com/admin/authkeys. See: https://tailscale.com/kb/1085/auth-keys#generate-an-auth-key for instructions. + +# Time Zone setting for containers +TZ=UTC # See: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones + +# Database Configuration (MariaDB) +MYSQL_ROOT_PASSWORD= # Set a strong root password +MYSQL_DATABASE=nextcloud +MYSQL_USER=nextcloud +MYSQL_PASSWORD= # Set a strong database password + +# Nextcloud Admin Credentials (auto-created on first run) +NEXTCLOUD_ADMIN_USER=admin +NEXTCLOUD_ADMIN_PASSWORD= # Set a strong admin password +NEXTCLOUD_TRUSTED_DOMAINS= # Set to your Nextcloud domain (e.g., nextcloud.your-tailnet.ts.net) + +# Optional Service variables +# PUID=1000 +# PHP_MEMORY_LIMIT=512M +# PHP_UPLOAD_LIMIT=512M + +#EXAMPLE_VAR="Environment varibale" diff --git a/services/nextcloud/README.md b/services/nextcloud/README.md new file mode 100644 index 0000000..2cb46b8 --- /dev/null +++ b/services/nextcloud/README.md @@ -0,0 +1,61 @@ +# Nextcloud with Tailscale Sidecar Configuration + +This Docker Compose configuration sets up [Nextcloud](https://github.com/nextcloud/docker) with Tailscale as a sidecar container to securely access your files and collaboration tools over your Tailnet. By using Tailscale in a sidecar configuration, you get automatic HTTPS with a URL like `https://nextcloud.your-tailnet.ts.net` without exposing anything to the public internet. + +## Nextcloud + +[Nextcloud](https://nextcloud.com) is a self-hosted platform for file sync, sharing, and collaboration. It provides a web interface for documents, calendars, contacts, and media, along with desktop and mobile apps for all major platforms. When paired with Tailscale, your Nextcloud instance becomes accessible across all your trusted devices through your secure Tailnet, with no need for port forwarding or complex reverse proxy configurations. + +## Configuration Overview + +In this setup, the `tailscale-nextcloud` service runs Tailscale, which manages secure networking for the Nextcloud stack. The `nextcloud`, `db`, and `redis` services all use the Tailscale network stack via Docker's `network_mode: service:` configuration. This keeps your entire Nextcloud stack Tailnet-only unless you intentionally expose ports. + +This stack includes four containers: +- **Tailscale** - Manages networking and exposes Nextcloud via Tailscale Serve with automatic HTTPS +- **Nextcloud** - The application server (Apache image, port 80) +- **MariaDB** - Database backend (LTS release) +- **Redis** - Caching and file locking to prevent performance issues + +## Prerequisites + +- Docker and Docker Compose installed +- A Tailscale auth key from the [Tailscale Admin Console](https://login.tailscale.com/admin/settings/keys) +- Your host user should be in the `docker` group + +## Getting Started + +1. Copy `templates/service-template` into `services/nextcloud` (or clone this repo) +2. Edit the `.env` file and set strong passwords for: + - `MYSQL_ROOT_PASSWORD` + - `MYSQL_PASSWORD` + - `NEXTCLOUD_ADMIN_PASSWORD` +3. Set your Tailscale auth key in `TS_AUTHKEY` +4. Run `docker compose up -d` +5. Access Nextcloud at `https://nextcloud.your-tailnet.ts.net` from any device on your Tailnet + +## Trusted Domains + +The `NEXTCLOUD_TRUSTED_DOMAINS` variable in `compose.yaml` is set to `${SERVICE}.tail12345.ts.net` by default. You should update this to match your actual Tailscale domain. You can find your domain in the Tailscale Admin Console under **DNS** settings. + +## Volumes + +| Volume | Purpose | +|--------|---------| +| `./nextcloud-data/html` | Nextcloud application and configuration files | +| `./nextcloud-data/db` | MariaDB database files | + +Pre-creating these directories is optional; Docker will create them automatically with root ownership. + +## Files to check + +Please check the following contents for validity as some variables need to be defined upfront. + +- `.env` + - Required: `TS_AUTHKEY` + - Required: `MYSQL_ROOT_PASSWORD`, `MYSQL_PASSWORD`, `NEXTCLOUD_ADMIN_PASSWORD` - change all from defaults + +## Upstream Documentation + +- [Nextcloud Docker Documentation](https://github.com/nextcloud/docker) +- [Nextcloud Admin Manual](https://docs.nextcloud.com/server/latest/admin_manual/) +- [Nextcloud Docker Compose Examples](https://github.com/nextcloud/docker#usage) diff --git a/services/nextcloud/compose.yaml b/services/nextcloud/compose.yaml new file mode 100644 index 0000000..86dd6d5 --- /dev/null +++ b/services/nextcloud/compose.yaml @@ -0,0 +1,114 @@ +configs: + ts-serve: + content: | + {"TCP":{"443":{"HTTPS":true}}, + "Web":{"$${TS_CERT_DOMAIN}:443": + {"Handlers":{"/": + {"Proxy":"http://127.0.0.1:80"}}}}, + "AllowFunnel":{"$${TS_CERT_DOMAIN}:443":false}} + +services: +# Make sure you have updated/checked the .env file with the correct variables. +# All the ${ xx } need to be defined there. + # Tailscale Sidecar Configuration + tailscale: + image: tailscale/tailscale:latest # Image to be used + container_name: tailscale-${SERVICE} # Name for local container management + hostname: ${SERVICE} # Name used within your Tailscale environment + environment: + - TS_AUTHKEY=${TS_AUTHKEY} + - TS_STATE_DIR=/var/lib/tailscale + - TS_SERVE_CONFIG=/config/serve.json # Tailscale Serve configuration to expose the web interface on your local Tailnet - remove this line if not required + - TS_USERSPACE=false + - TS_ENABLE_HEALTH_CHECK=true # Enable healthcheck endpoint: "/healthz" + - TS_LOCAL_ADDR_PORT=127.0.0.1:41234 # The : for the healthz endpoint + #- TS_ACCEPT_DNS=true # Uncomment when using MagicDNS + - TS_AUTH_ONCE=true + configs: + - source: ts-serve + target: /config/serve.json + volumes: + - ./config:/config # Config folder used to store Tailscale files - you may need to change the path + - ./ts/state:/var/lib/tailscale # Tailscale requirement - you may need to change the path + devices: + - /dev/net/tun:/dev/net/tun # Network configuration for Tailscale to work + cap_add: + - net_admin # Tailscale requirement + #ports: + # - 0.0.0.0:${SERVICEPORT}:${SERVICEPORT} # Binding port ${SERVICEPORT} to the local network - may be removed if only exposure to your Tailnet is required + # If any DNS issues arise, use your preferred DNS provider by uncommenting the config below + #dns: + # - ${DNS_SERVER} + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://127.0.0.1:41234/healthz"] # Check Tailscale has a Tailnet IP and is operational + interval: 1m # How often to perform the check + timeout: 10s # Time to wait for the check to succeed + retries: 3 # Number of retries before marking as unhealthy + start_period: 10s # Time to wait before starting health checks + restart: always + + # ${SERVICE} + application: + image: ${IMAGE_URL} # Image to be used + network_mode: service:tailscale # Sidecar configuration to route ${SERVICE} through Tailscale + container_name: app-${SERVICE} # Name for local container management + environment: + - MYSQL_HOST=127.0.0.1 + - MYSQL_DATABASE=${MYSQL_DATABASE} + - MYSQL_USER=${MYSQL_USER} + - MYSQL_PASSWORD=${MYSQL_PASSWORD} + - REDIS_HOST=localhost + - REDIS_HOST_PORT=6379 + - NEXTCLOUD_ADMIN_USER=${NEXTCLOUD_ADMIN_USER} + - NEXTCLOUD_ADMIN_PASSWORD=${NEXTCLOUD_ADMIN_PASSWORD} + - NEXTCLOUD_TRUSTED_DOMAINS=${NEXTCLOUD_TRUSTED_DOMAINS} + - OVERWRITEPROTOCOL=https + - TZ=${TZ} + volumes: + - ./${SERVICE}-data/html:/var/www/html + depends_on: + db: + condition: service_healthy + redis: + condition: service_healthy + tailscale: + condition: service_healthy + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost/status.php"] # Check if Nextcloud status endpoint responds + interval: 1m # How often to perform the check + timeout: 10s # Time to wait for the check to succeed + retries: 3 # Number of retries before marking as unhealthy + start_period: 60s # Time to wait before starting health checks - Nextcloud needs time to initialize + restart: always + + db: + image: mariadb:lts # MariaDB LTS for production use + network_mode: service:tailscale # Sidecar configuration to route ${SERVICE} through Tailscale + container_name: app-${SERVICE}-database # Name for local container management + command: --transaction-isolation=READ-COMMITTED --log-bin=binlog --binlog-format=ROW # Required MariaDB configuration for Nextcloud + environment: + - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} + - MYSQL_DATABASE=${MYSQL_DATABASE} + - MYSQL_USER=${MYSQL_USER} + - MYSQL_PASSWORD=${MYSQL_PASSWORD} + volumes: + - ./${SERVICE}-data/db:/var/lib/mysql + healthcheck: + test: ["CMD", "mariadb-admin", "ping", "-h", "localhost"] # Check if MariaDB is responsive + interval: 1m # How often to perform the check + timeout: 10s # Time to wait for the check to succeed + retries: 3 # Number of retries before marking as unhealthy + start_period: 30s # Time to wait before starting health checks + restart: always + + redis: + image: redis:alpine # Redis for caching and file locking + network_mode: service:tailscale # Sidecar configuration to route ${SERVICE} through Tailscale + container_name: app-${SERVICE}-redis # Name for local container management + healthcheck: + test: ["CMD", "redis-cli", "ping"] # Check if Redis responds to ping + interval: 1m # How often to perform the check + timeout: 10s # Time to wait for the check to succeed + retries: 3 # Number of retries before marking as unhealthy + start_period: 10s # Time to wait before starting health checks + restart: always diff --git a/services/tailscale-exit-wireguard/.env b/services/tailscale-exit-wireguard/.env new file mode 100644 index 0000000..d308b16 --- /dev/null +++ b/services/tailscale-exit-wireguard/.env @@ -0,0 +1,17 @@ +#version=1.1 + +# Service Configuration +SERVICE=exit-wireguard + +# Tailscale Configuration +TS_AUTHKEY= # Your Tailscale auth key from https://login.tailscale.com/admin/settings/keys + +# WireGuard (ProtonVPN) Configuration +# Place your ProtonVPN WireGuard config file at ./wireguard/config/wg0.conf + +# Time Zone setting for containers +TZ=Europe/Amsterdam # See: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones + +# Optional Service variables +PUID=1000 +PGID=1000 diff --git a/services/tailscale-exit-wireguard/README.md b/services/tailscale-exit-wireguard/README.md new file mode 100644 index 0000000..4e7e834 --- /dev/null +++ b/services/tailscale-exit-wireguard/README.md @@ -0,0 +1,110 @@ +# Tailscale Exit Node with WireGuard (ProtonVPN) Sidecar + +This Docker Compose configuration sets up a Tailscale Exit Node that routes all traffic through a WireGuard VPN tunnel (e.g., ProtonVPN). Devices on your Tailscale network can use this node as an exit point, ensuring all their internet traffic is encrypted and routed through your VPN provider. + +## Architecture + +``` +Device → Tailscale Exit Node → WireGuard (ProtonVPN) → Internet +``` + +- **WireGuard** runs the ProtonVPN tunnel as a VPN client +- **Tailscale** advertises itself as an exit node, routing traffic through WireGuard +- When you select this exit node on any Tailscale device, your traffic flows through ProtonVPN + +## Configuration Overview + +### WireGuard Service + +- Uses `linuxserver/wireguard` image to run the ProtonVPN WireGuard tunnel as a **client** +- Requires your ProtonVPN WireGuard config file at `./wireguard/config/wg0.conf` +- No ports are exposed - WireGuard connects outbound to ProtonVPN only +- Handles IP forwarding and NAT for VPN traffic + +### Tailscale Exit Node + +- Uses `network_mode: service:wireguard` to share WireGuard's network namespace +- Advertises itself as an exit node via `--advertise-exit-node` +- All Tailscale traffic is routed through the WireGuard tunnel + +## Setup Instructions + +### 1. Get a Tailscale Auth Key + +1. Go to [Tailscale Admin](https://login.tailscale.com/admin/settings/keys) +2. Generate a new auth key +3. Set `TS_AUTHKEY` in the `.env` file + +### 2. Get Your ProtonVPN WireGuard Config + +1. Go to [ProtonVPN Dashboard](https://account.protonvpn.com/vpn/dashboard) +2. Download the WireGuard configuration for your desired server +3. Create the config directory and place the file: + +```bash +mkdir -p wireguard/config +cp /path/to/your/protonvpn-wg0.conf ./wireguard/config/wg0.conf +``` + +### 3. Configure Environment Variables + +Edit the `.env` file with your settings: + +| Variable | Description | Default | +|----------|-------------|---------| +| `TS_AUTHKEY` | Tailscale auth key (required) | - | +| `TZ` | Timezone | `Europe/Amsterdam` | + +### 4. Enable Exit Node on Tailscale + +After starting the stack: + +1. Go to [Tailscale Admin](https://login.tailscale.com/admin/machines) +2. Find your exit node machine +3. Click the `...` menu → **Edit route settings** +4. Enable the exit node + +### 5. Use the Exit Node + +On any Tailscale device: + +```bash +# Linux/macOS +tailscale up --exit-node= + +# Or select it in the Tailscale app UI +``` + +## File Structure + +``` +tailscale-exit-wireguard/ +├── compose.yaml # Docker Compose configuration +├── .env # Environment variables +├── README.md # This file +├── wireguard/ +│ └── config/ +│ └── wg0.conf # Your ProtonVPN WireGuard config +└── ts/ + └── state/ # Tailscale state (auto-created) +``` + +## Troubleshooting + +### WireGuard not connecting + +- Verify your `wg0.conf` is valid: `docker compose exec wireguard cat /config/wg0.conf` +- Check WireGuard logs: `docker compose logs wireguard` +- Ensure the config file has no BOM or Windows line endings + +### Tailscale not advertising exit node + +- Check Tailscale status: `docker compose exec tailscale tailscale status` +- Verify exit node is enabled in Tailscale admin panel +- Check Tailscale logs: `docker compose logs tailscale` + +### Health check failing + +- WireGuard health check runs `wg show wg0` to verify the interface is up +- Tailscale health check hits `http://127.0.0.1:41234/healthz` +- Both services must be healthy for the stack to work correctly diff --git a/services/tailscale-exit-wireguard/compose.yaml b/services/tailscale-exit-wireguard/compose.yaml new file mode 100644 index 0000000..994c149 --- /dev/null +++ b/services/tailscale-exit-wireguard/compose.yaml @@ -0,0 +1,60 @@ +services: +# Make sure you have updated/checked the .env file with the correct variables. +# All the ${ xx } need to be defined there. + + # WireGuard (ProtonVPN) - Runs the VPN tunnel + wireguard: + image: linuxserver/wireguard:latest + container_name: app-wireguard + cap_add: + - net_admin + - sys_module + environment: + - PUID=${PUID:-1000} + - PGID=${PGID:-1000} + - TZ=${TZ} + volumes: + - ./wireguard/config:/config + devices: + - /dev/net/tun:/dev/net/tun + sysctls: + - net.ipv4.conf.all.src_valid_mark=1 + - net.ipv4.ip_forward=1 + healthcheck: + test: ["CMD", "wg", "show", "wg0"] + interval: 1m + timeout: 10s + retries: 3 + start_period: 30s + restart: always + + # Tailscale Exit Node - Routes traffic through WireGuard + tailscale: + image: tailscale/tailscale:latest + container_name: tailscale-${SERVICE} + network_mode: service:wireguard + depends_on: + wireguard: + condition: service_healthy + environment: + - TS_AUTHKEY=${TS_AUTHKEY} + - TS_STATE_DIR=/var/lib/tailscale + - TS_EXTRA_ARGS=--advertise-exit-node + - TS_USERSPACE=false + - TS_ENABLE_HEALTH_CHECK=true + - TS_LOCAL_ADDR_PORT=127.0.0.1:41234 + #- TS_ACCEPT_DNS=true + - TS_AUTH_ONCE=true + volumes: + - ./ts/state:/var/lib/tailscale + devices: + - /dev/net/tun:/dev/net/tun + cap_add: + - net_admin + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://127.0.0.1:41234/healthz"] + interval: 1m + timeout: 10s + retries: 3 + start_period: 10s + restart: always