Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion services/coder/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ This stack runs Coder with a Tailscale sidecar, as described in [the standard se

## Deviations from the standard setup

- **Extra container.** The stack runs a `database` container with PostgreSQL. It uses the network of the `tailscale` container as well, so Coder reaches it at `localhost`. PostgreSQL therefore also listens on port `5432` of the Tailscale IP address of the device.
- **Extra container.** The stack runs a `database` container with PostgreSQL. It uses the network of the `tailscale` container as well, so Coder reaches it at `localhost`. PostgreSQL listens only on the loopback address of the device, so other devices on your Tailnet cannot reach it.
- **Docker socket.** Coder mounts `/var/run/docker.sock` read-only, so that templates can use Docker on the host. The `:ro` flag only makes the socket file read-only. It does not limit what the service can do through the Docker API, so treat access to the socket as root access to the Docker host.
- **Image version.** `CODER_VERSION` in `.env` selects the version of the Coder image.

Expand All @@ -43,6 +43,8 @@ Open the web interface and create the first account, which becomes the administr

Earlier versions of this stack had a sample value for `POSTGRES_PASSWORD` in `.env`. It is now empty, and Compose stops with an error until you set it. If you already run the stack, keep the values that you use now. This is required for the database password, because the database applies it only at the first start. If you kept the sample value, set `POSTGRES_PASSWORD=strongpassword` again. The database still uses it.

Earlier versions listened on all addresses of the device, so PostgreSQL was reachable from your Tailnet. This version makes it listen on localhost only. Your data stays in place. Run `docker compose up -d` to recreate the `database` container. Any tool that connects to the database from another device stops working.

## Links

- [Coder documentation](https://coder.com/docs)
Expand Down
1 change: 1 addition & 0 deletions services/coder/compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ services:
# Minimum supported version is 13.
# More versions here: https://hub.docker.com/_/postgres
image: "postgres:17"
command: ["postgres", "-c", "listen_addresses=localhost"] # Listen on localhost only, so the Tailnet cannot reach the database
environment:
POSTGRES_USER: ${POSTGRES_USER:-username} # The PostgreSQL user (useful to connect to the database)
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env} # The PostgreSQL password (useful to connect to the database)
Expand Down
4 changes: 3 additions & 1 deletion services/docmost/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Set these values in `.env`. Compose stops with an error if one of them is empty.

## Deviations from the standard setup

- **Extra containers.** The stack runs `db` (PostgreSQL) and `redis`. Both use the network of the `tailscale` container as well, so Docmost reaches them at `localhost`. PostgreSQL and Redis therefore also listen on ports `5432` and `6379` of the Tailscale IP address of the device.
- **Extra containers.** The stack runs `db` (PostgreSQL) and `redis`. Both use the network of the `tailscale` container as well, so Docmost reaches them at `localhost`. PostgreSQL and Redis listen only on the loopback address of the device, so other devices on your Tailnet cannot reach them.
- **Application address.** `APP_URL` in `compose.yaml` is `http://localhost:3000`. Docmost uses this value for the links that it generates, for example in emails. Change it to `https://docmost.<tailnet>.ts.net` if you use such links.

## First run
Expand All @@ -37,6 +37,8 @@ Open the web interface. Docmost shows its setup page, where you create your work

If your `compose.yaml` contained the secret and the database password before, set `APP_SECRET` and `DB_PASSWORD` in `.env` to those same values.

Earlier versions listened on all addresses of the device, so PostgreSQL and Redis were reachable from your Tailnet. This version makes them listen on localhost only. Your data stays in place. Run `docker compose up -d` to recreate the `db` and `redis` containers. Any tool that connects to the database or to Redis from another device stops working.

## Links

- [Docmost documentation](https://docmost.com/docs/)
Expand Down
2 changes: 2 additions & 0 deletions services/docmost/compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ services:
image: postgres:16-alpine
network_mode: service:tailscale # Sidecar configuration to route the service through Tailscale
container_name: app-${SERVICE}-database # Name for local container management
command: ["postgres", "-c", "listen_addresses=localhost"] # Listen on localhost only, so the Tailnet cannot reach the database
environment:
POSTGRES_DB: docmost
POSTGRES_USER: docmost
Expand All @@ -97,6 +98,7 @@ services:
image: redis:7.2-alpine
network_mode: service:tailscale # Sidecar configuration to route the service through Tailscale
container_name: app-${SERVICE}-redis # Name for local container management
command: ["redis-server", "--bind", "127.0.0.1", "-::1"] # Listen on localhost only, so the Tailnet cannot reach Redis
healthcheck:
test: ["CMD", "redis-cli", "ping"] # Check if Redis responds
interval: 10s # How often to perform the check
Expand Down
4 changes: 3 additions & 1 deletion services/kaneo/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Set these values in `.env`:

## Deviations from the standard setup

- **Extra container.** The stack runs a `postgres` container. It uses the network of the `tailscale` container as well, so Kaneo reaches it at `localhost`. PostgreSQL therefore also listens on port `5432` of the Tailscale IP address of the device.
- **Extra container.** The stack runs a `postgres` container. It uses the network of the `tailscale` container as well, so Kaneo reaches it at `localhost`. PostgreSQL listens only on the loopback address of the device, so other devices on your Tailnet cannot reach it.
- **Serve port from `.env`.** The Tailscale Serve configuration in `compose.yaml` takes its port from `SERVICEPORT`.
- **Database image.** `IMAGE_URL_DATABASE` in `.env` selects the PostgreSQL image.
- **The containers read the whole `.env` file.** Both containers load `.env` through `env_file`. Every variable in that file, including `TS_AUTHKEY`, is therefore present in their environment.
Expand All @@ -38,6 +38,8 @@ Since [release v2.7.0](https://github.com/usekaneo/kaneo/releases/tag/v2.7.0), K

When you update from such a version, start the stack with `docker compose up -d --remove-orphans`. Compose then removes the old `frontend` and `backend` containers, which use the same ports as the new container.

Earlier versions listened on all addresses of the device, so PostgreSQL was reachable from your Tailnet. This version makes it listen on localhost only. Your data stays in place. Run `docker compose up -d` to recreate the `postgres` container. Any tool that connects to the database from another device stops working.

## Links

- [Kaneo documentation](https://kaneo.app/docs)
Expand Down
1 change: 1 addition & 0 deletions services/kaneo/compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ services:
image: ${IMAGE_URL_DATABASE} # Image to be used
network_mode: service:tailscale # Sidecar configuration to route the service through Tailscale
container_name: app-${SERVICE}-postgres # Name for local container management
command: ["postgres", "-c", "listen_addresses=localhost"] # Listen on localhost only, so the Tailnet cannot reach the database
env_file:
- .env
environment:
Expand Down
6 changes: 5 additions & 1 deletion services/miniflux/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,17 @@ Set these values in `.env`:

## Deviations from the standard setup

- **Extra container.** The stack runs a `db` container with PostgreSQL. It uses the network of the `tailscale` container as well, so Miniflux reaches it at `localhost`. PostgreSQL therefore also listens on port `5432` of the Tailscale IP address of the device.
- **Extra container.** The stack runs a `db` container with PostgreSQL. It uses the network of the `tailscale` container as well, so Miniflux reaches it at `localhost`. PostgreSQL listens only on the loopback address of the device, so other devices on your Tailnet cannot reach it.
- **Automatic setup.** `RUN_MIGRATIONS=1` and `CREATE_ADMIN=1` make Miniflux prepare the database and create the administrator at the start.

## First run

Open the web interface and log in with the administrator account from `.env`.

## Upgrading

Earlier versions listened on all addresses of the device, so PostgreSQL was reachable from your Tailnet. This version makes it listen on localhost only. Your data stays in place. Run `docker compose up -d` to recreate the `db` container. Any tool that connects to the database from another device stops working.

## Links

- [Miniflux documentation](https://miniflux.app/docs/)
Expand Down
1 change: 1 addition & 0 deletions services/miniflux/compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ services:
image: postgres:15-alpine
network_mode: service:tailscale # Join the same network namespace to be accessible via localhost
container_name: app-${SERVICE}-db
command: ["postgres", "-c", "listen_addresses=localhost"] # Listen on localhost only, so the Tailnet cannot reach the database
environment:
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env}
Expand Down
4 changes: 2 additions & 2 deletions services/pocket-id/.env
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ APP_URL=https://pocket-id.<YOUR-TAILSCALE-DOMAIN>.ts.net

# Encryption key (choose one method):
# Method 1: Direct key (simple but less secure)
# Generate with: openssl rand -base64 32
# Required: encrypts the stored data, such as the token signing keys. Generate it with: openssl rand -base64 32
ENCRYPTION_KEY=
# Method 2: File-based key (recommended)
# Put the base64 key in a file and point to it here.
# Put the key in a file and point to it here. The file wins over ENCRYPTION_KEY, but Compose still requires ENCRYPTION_KEY to be set.
# ENCRYPTION_KEY_FILE=/path/to/encryption_key

# These variables are optional but recommended to review:
Expand Down
6 changes: 5 additions & 1 deletion services/pocket-id/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ This stack runs Pocket ID with a Tailscale sidecar, as described in [the standar

- **Enable HTTPS certificates.** HTTPS certificates must be [enabled for your Tailnet](https://console.tailscale.com/admin/dns) (**DNS** > **HTTPS Certificates**). Passkeys only work over HTTPS.
- **Set `APP_URL` in `.env`.** Use the address of the web interface, `https://pocket-id.<tailnet>.ts.net`. Pocket ID uses it for its OIDC issuer, its endpoints, and passkeys, and it does not start with the sample value.
- **Set `ENCRYPTION_KEY` in `.env`.** Generate the key with `openssl rand -base64 32`.
- **Set `ENCRYPTION_KEY` in `.env`.** Generate the key with `openssl rand -base64 32`. Compose stops with an error if it is empty.

## Deviations from the standard setup

Expand All @@ -37,6 +37,10 @@ Open `https://pocket-id.<tailnet>.ts.net/setup` to create the administrator acco
- **Custom domains.** Tailscale Serve only serves the `ts.net` name of the device. A custom domain in `APP_URL` needs your own DNS and reverse proxy, which this stack does not include.
- **Local network access.** The `ports` block stays commented out. If you enable it, the stack publishes plain HTTP on the Docker host, where passkeys do not work.

## Upgrading

Earlier versions of this stack had an empty `ENCRYPTION_KEY` in `.env`, and Compose started the stack without an error. Pocket ID then stopped at start. Compose now stops with an error until you set `ENCRYPTION_KEY`. If you already run the stack with a key, keep the value that you use now, because Pocket ID encrypts its stored data with it. If you use `ENCRYPTION_KEY_FILE`, set `ENCRYPTION_KEY` as well, because Compose still requires it.

## Links

- [Pocket ID installation](https://pocket-id.org/docs/setup/installation)
Expand Down
2 changes: 2 additions & 0 deletions services/pocket-id/compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ services:
network_mode: service:tailscale # Sidecar configuration to route the service through Tailscale
container_name: app-${SERVICE} # Name for local container management
env_file: .env
environment:
- ENCRYPTION_KEY=${ENCRYPTION_KEY:?Set ENCRYPTION_KEY in .env}
volumes:
- ./${SERVICE}-data:/app/data
depends_on:
Expand Down
4 changes: 3 additions & 1 deletion services/tandoor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Set these values in `.env`:

## Deviations from the standard setup

- **Extra container.** The stack runs a `database` container with PostgreSQL. It uses the network of the `tailscale` container as well, so Tandoor reaches it at `127.0.0.1`. PostgreSQL therefore also listens on port `5432` of the Tailscale IP address of the device.
- **Extra container.** The stack runs a `database` container with PostgreSQL. It uses the network of the `tailscale` container as well, so Tandoor reaches it at `127.0.0.1`. PostgreSQL listens only on the loopback address of the device, so other devices on your Tailnet cannot reach it.
- **Service port.** `TANDOOR_PORT` makes Tandoor listen on the port from `SERVICEPORT`, which is `9001`.
- **The container reads the whole `.env` file.** The `application` container loads `.env` through `env_file`. Every variable in that file, including `TS_AUTHKEY`, is therefore present in its environment.

Expand All @@ -38,6 +38,8 @@ The first start can take a few minutes, because Tandoor prepares its database. T

Earlier versions of this stack had sample values for `SECRET_KEY` and `POSTGRES_PASSWORD` in `.env`. They are now empty, and Compose stops with an error until you set them. If you already run the stack, keep the values that you use now. This is required for the database password, because the database applies it only at the first start. If you kept the sample value, set `POSTGRES_PASSWORD=REPLACE_WITH_RANDOM_ALPHANUMERIC_PASSWORD` again. The database still uses it.

Earlier versions listened on all addresses of the device, so PostgreSQL was reachable from your Tailnet. This version makes it listen on localhost only. Your data stays in place. Run `docker compose up -d` to recreate the `database` container. Any tool that connects to the database from another device stops working.

## Links

- [Tandoor Recipes documentation](https://docs.tandoor.dev/)
Expand Down
1 change: 1 addition & 0 deletions services/tandoor/compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ services:
image: postgres:16-alpine
network_mode: service:tailscale
container_name: app-${SERVICE}-database
command: ["postgres", "-c", "listen_addresses=localhost"] # Listen on localhost only, so the Tailnet cannot reach the database
environment:
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env}
POSTGRES_USER: ${POSTGRES_USER}
Expand Down
30 changes: 27 additions & 3 deletions services/traefik/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,46 @@ Nothing beyond the [Quick Start](../../README.md#quick-start).

- **Published host port.** The `ports` block is active and publishes port `80` of the Docker host. Devices in your local network can therefore reach Traefik without Tailscale.
- **Service name.** The application service is called `traefik_proxy`, not `application`.
- **Docker socket.** Traefik mounts `/var/run/docker.sock` to discover containers and their labels.
- **Docker socket.** Traefik mounts `/var/run/docker.sock` with write access to discover containers and their labels. Treat access to the socket as root access to the Docker host.
- **Configuration through flags.** The `command` block in `compose.yaml` is the static configuration. Traefik ignores these flags when it finds a static configuration file, so edit the flags and do not add a `traefik.yml` file.
- **Sample site.** The stack runs a `simpleweb` container with routing labels as an example. Replace it with your own services.
- **Only port 80.** Tailscale Serve listens on port `443` of the Tailnet address and forwards to the `web` entrypoint of Traefik on port `80`. Do not add a Traefik entrypoint on port `443`. Traefik shares the network of the `tailscale` container, where that port is in use, so Traefik would exit and restart in a loop.
- **Health check.** The health check calls the ping endpoint, so keep the `--ping=true` flag. Traefik only routes to containers that Docker reports as healthy, so the sample site is reachable only after its first health check passes.
- **Dashboard.** The stack does not serve the Traefik dashboard or its API. Serving them needs a router with a password, which is described in [Configuration](#configuration).

## First run

Requests through your Tailnet arrive with the host name `traefik.<tailnet>.ts.net`. The sample routers match `traefik.domain.local` and `simpleweb.domain.local`, so Traefik answers `404` over the Tailnet at first.
Requests through your Tailnet arrive with the host name `traefik.<tailnet>.ts.net`. The sample router matches `simpleweb.domain.local`, so Traefik answers `404` over the Tailnet at first.

Change a `Host()` rule in the labels in `compose.yaml` to `traefik.<tailnet>.ts.net` and restart the stack. That router is then reachable at `https://traefik.<tailnet>.ts.net`.
Change the `Host()` rule of the `simpleweb` router in the labels in `compose.yaml` to `traefik.<tailnet>.ts.net` and restart the stack. The sample site is then reachable at `https://traefik.<tailnet>.ts.net`.

## Configuration

**Dashboard.** Add a router that uses the `api@internal` service, and protect it with a basic-auth middleware. Create the password entry with `htpasswd -nB <user>`, and write each `$` of the hash as `$$`, because Compose reads `$` in labels as a variable. Add these labels to the `traefik_proxy` service in `compose.yaml`:

```yaml
labels:
- traefik.enable=true
- traefik.http.routers.dashboard.rule=Host(`traefik.<tailnet>.ts.net`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))
- traefik.http.routers.dashboard.entrypoints=web
- traefik.http.routers.dashboard.service=api@internal
- traefik.http.services.dashboard.loadbalancer.server.port=8080 # Traefik drops the labels of a container without a port - this label gives it one
- traefik.http.routers.dashboard.middlewares=dashboard-auth
- traefik.http.middlewares.dashboard-auth.basicauth.users=<user>:<escaped hash>
```

Restart the stack, then open `https://traefik.<tailnet>.ts.net/dashboard/` and sign in with the user from the hash. Without a router, the dashboard and the API are not served.

Sign in through the Tailnet address only. The `ports` block publishes port `80` of the Docker host over plain HTTP, so a device on your local network that sends the Tailnet host name to that port reaches the sign-in prompt without TLS.

## Troubleshooting

Traefik writes its log to `./traefik-data/log/traefik.log`, so `docker logs` shows nothing for the Traefik container. Read that file when the container restarts or a router does not work.

## Upgrading

Earlier versions of this stack served the Traefik dashboard and API without a password. They were reachable on port `8080` of the Tailscale IP address, and on port `80` of the Docker host for the host name `traefik.domain.local`. This version serves neither. The dashboard router and the geoblock plugin flags are removed. Start the stack with `docker compose up -d`. No new variable is required. If you used the dashboard, add the router from [Configuration](#configuration).

## Links

- [Traefik documentation](https://doc.traefik.io/traefik/)
Expand Down
9 changes: 1 addition & 8 deletions services/traefik/compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ services:
- ./${SERVICE}-data/log/:/var/log/
- /var/run/docker.sock:/var/run/docker.sock #Required for the Service Connections
command: # Static configuration. Traefik ignores these flags if it finds a traefik.yml file.
- "--api.insecure=true"
- "--api.dashboard=true" # Enables the API and dashboard for a router you add; no router serves them by default - see the README
- "--ping=true" # Required by the health check
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
Expand All @@ -79,13 +79,6 @@ services:
- --log.filepath=/var/log/traefik.log
- --accesslog=true
- --accesslog.filepath=/var/log/traefik.access.log
- --experimental.plugins.traefik-plugin-geoblock.modulename=github.com/nscuro/traefik-plugin-geoblock
- --experimental.plugins.traefik-plugin-geoblock.version=v0.14.0
labels:
- traefik.enable=true
- traefik.http.routers.mydashboard.rule=Host(`traefik.domain.local`)
- traefik.http.routers.mydashboard.service=api@internal
- traefik.http.services.mydashboard.loadbalancer.server.port=8080 # Traefik drops the labels of a container without a port - this container exposes none, because it uses the Tailscale network

simpleweb:
image: yeasy/simple-web:latest
Expand Down
Loading