Skip to content
Merged
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
36 changes: 34 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,40 @@ Thanks for helping improve these Tailscale sidecar examples.

6. Complete the service README.

Document prerequisites, persistent paths, setup steps, ports, Tailnet access,
and service-specific exceptions. Link to the upstream documentation.
Every service README uses the headings of the template, in the same order.
Do not rename them and do not add others, so that every service reads the
same way. Replace the placeholders in capitals.

- **Introduction.** Say what the service does in one or two sentences and
link to the upstream project. Leave feature lists to upstream.
- **At a glance.** Give the Tailnet address, the port that Tailscale Serve
forwards to, the image, and the data paths on the host. Add a row for
each further port that users connect to, such as DNS or SMTP.
- **Before you start.** List only what the Quick Start does not cover:
values that must change in `.env`, secrets to generate, folders to
create, and required host groups or devices.
- **Deviations from the standard setup.** List every difference from
[the standard setup](documentation/standard-setup.md) and give the
reason. Examples are extra containers, published host ports, a changed
or removed Serve configuration, DNS settings, and added capabilities.
- **First run.** Describe what the user does after the first start, such
as creating the first account or finding a generated password.
- **Links.** Link to the upstream documentation and source code.

When a section has nothing to report, keep the heading and the sentence
from the template. A reader can then tell that the service follows the
standard setup, and that the section was not forgotten.

Add these optional sections between "First run" and "Links" when the
service needs them, in this order:

- **Configuration.** Optional settings that users commonly change.
- **Troubleshooting.** Known errors and their solutions.
- **Upgrading.** Steps for users of an older version of the stack.

State only what you confirmed in the Compose file, the upstream
documentation, or a running stack. Put guidance that applies to more than
one service in `documentation/` and link to it.

7. Add the service to the correct category in the root `README.md`.

Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ ScaleTail provides ready-to-run [Docker Compose](https://docs.docker.com/compose
docker compose up -d
```

Every stack starts from the same [standard setup](documentation/standard-setup.md), which explains the containers, the Tailnet address, and the settings in `.env`.

## Table of Contents

- [ScaleTail - Secure Self-Hosting Made Simple](#scaletail---secure-self-hosting-made-simple)
Expand Down
40 changes: 40 additions & 0 deletions documentation/free-up-port-53.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Free up port 53 on the Docker host

A DNS service such as Pi-hole or AdGuard Home needs port 53. This page applies only when you publish port 53 on the Docker host, for example to offer DNS to your local network. A stack that is reachable over your Tailnet only does not need it.

## Why port 53 is in use

On Debian-based systems that use `systemd-resolved`, such as Ubuntu Server 22.04 and 24.04, a DNS stub listener runs by default. It listens on `127.0.0.53:53` and answers DNS queries from local applications. A container that publishes port 53 on all host addresses then fails to start, because the port is already taken.

The `DNSStubListener` option in `/etc/systemd/resolved.conf` controls this listener:

- `DNSStubListener=yes`: `systemd-resolved` listens on `127.0.0.53:53`. This is the default.
- `DNSStubListener=no`: `systemd-resolved` does not listen on port 53, so another DNS service can use it.

## Steps

1. Open the configuration file:

```bash
sudo nano /etc/systemd/resolved.conf
```

2. Find the line `#DNSStubListener=yes` and change it to:

```ini
DNSStubListener=no
```

3. Restart the service:

```bash
sudo systemctl restart systemd-resolved
```

4. Check that port 53 is free:

```bash
sudo ss -tuln | grep ':53 '
```

The command prints nothing when no process listens on port 53.
53 changes: 53 additions & 0 deletions documentation/standard-setup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# The standard ScaleTail setup

Every service in this repository starts from the same [template](../templates/service-template/). This page describes that shared setup once. The README of a service only lists what differs, under the heading "Deviations from the standard setup".

## Containers

Each stack runs two containers that share one network namespace.

| Compose service | Container name | Purpose |
| --------------- | ----------------------- | -------------------------------------------------------------------- |
| `tailscale` | `tailscale-<service>` | Joins your Tailnet as a device named after `SERVICE` in `.env`. |
| `application` | `app-<service>` | Runs the service and uses the network of the `tailscale` container. |

The application uses `network_mode: service:tailscale` and starts only after the `tailscale` container reports healthy.

## Tailnet access

- **Web interface.** [Tailscale Serve](https://tailscale.com/kb/1312/serve) publishes the web interface at `https://<service>.<tailnet>.ts.net` and forwards it to the internal port of the application. `<service>` is the value of `SERVICE` and `<tailnet>` is your [Tailnet name](https://tailscale.com/kb/1217/tailnet-name).
- **Requirements.** Your Tailnet needs [MagicDNS](https://tailscale.com/kb/1081/magicdns) and [HTTPS certificates](https://tailscale.com/kb/1153/enabling-https) enabled. The first request can take up to a minute, because Tailscale requests the certificate at that moment.
- **Tailnet only.** Tailscale Funnel is disabled, so the service is not reachable from the public internet.
- **Other ports.** Any other port the application listens on is reachable at the Tailscale IP address of the device, when your Tailnet policy allows it.
- **No host ports.** The stack publishes no ports on the Docker host. To reach the service from your local network as well, uncomment the `ports` block of the `tailscale` service.

## Settings in `.env`

| Variable | Purpose |
| ------------- | -------------------------------------------------------------------------------------------- |
| `SERVICE` | Name of the Tailnet device, the containers, and the data folder. |
| `IMAGE_URL` | Image of the application. |
| `SERVICEPORT` | Port used by the optional `ports` block. The port for Tailscale Serve is set in `compose.yaml`. |
| `DNS_SERVER` | DNS server used by the optional `dns` block. |
| `TS_AUTHKEY` | Your Tailscale auth key. Only needed for the first start. |
| `TZ` | Time zone, passed to the application when its image supports it. |

## Data

All data stays in the service directory, next to `compose.yaml`.

| Path | Content |
| ------------------- | ----------------------------------------- |
| `./config` | Tailscale configuration files. |
| `./ts/state` | Tailscale state, including the device key. |
| `./<service>-data/` | Data of the application. |

Keep `./ts/state` when you recreate the stack. Without it, the device joins your Tailnet again as a new device.

## DNS

The containers use Docker's DNS by default, so the application can reach other containers in the same stack by their Compose service name. It cannot resolve MagicDNS names of other Tailnet devices.

- To reach another Tailnet device from the application, use its Tailscale IP address.
- To use MagicDNS names instead, uncomment `TS_ACCEPT_DNS=true`. This replaces Docker's DNS, so Compose service names no longer resolve. Do not use it in a stack where the application depends on other containers by name.
- If name resolution fails in general, uncomment the `dns` block to use the server from `DNS_SERVER`.
37 changes: 20 additions & 17 deletions services/actual-budget/README.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,31 @@
# Actual Budget with Tailscale Sidecar Configuration
# Actual Budget

This Docker Compose configuration sets up **Actual Budget** with a Tailscale sidecar container, enabling secure and private access to your personal finance app over your Tailnet. With this setup, your budgeting data stays fully private and is only accessible from trusted devices, without exposing anything to the public internet.
[Actual Budget](https://actualbudget.org/) is a personal finance app for budgeting. You track your accounts and spending and plan your budget, and your data stays on your own server.

## Actual Budget
This stack runs Actual Budget with a Tailscale sidecar, as described in [the standard setup](../../documentation/standard-setup.md).

[Actual Budget](https://github.com/actualbudget/actual) is an open-source, self-hosted personal finance and budgeting app focused on privacy and control. It serves as a modern alternative to tools like YNAB, allowing you to track spending, manage accounts, and plan budgets while retaining full ownership of your financial data.
## At a glance

When paired with Tailscale, Actual Budget becomes accessible across your devices through your secure Tailnet, eliminating the need for public exposure or complex reverse proxy configurations.
| Item | Value |
| ------------- | ---------------------------------------- |
| Web interface | `https://actual-budget.<tailnet>.ts.net` |
| Service port | `5006` |
| Image | `docker.io/actualbudget/actual-server` |
| Data | `./actual-budget-data` |

## Configuration Overview
## Before you start

In this setup, the `tailscale-actual` service runs Tailscale, which manages secure networking for Actual Budget. The `actual` service uses the Tailscale network stack via Docker's `network_mode: service:tailscale` configuration. This ensures the application is only reachable over your Tailnet unless you explicitly expose ports.
Nothing beyond the [Quick Start](../../README.md#quick-start).

## Key Features
## Deviations from the standard setup

- Self-hosted personal budgeting platform
- Privacy-first approach with full data ownership
- Sync across devices without relying on third-party cloud services
- Transaction tracking, budgeting, and reporting
- Secure Tailnet-only access via Tailscale
None.

## Files to check
## First run

Please check the following contents for validity as some variables need to be defined upfront.
Open the web interface and set a password for the server. Then create a budget file or import an existing one.

- `.env`
- Required: `TS_AUTHKEY`
## Links

- [Actual Budget documentation](https://actualbudget.org/docs/)
- [Actual Budget source code](https://github.com/actualbudget/actual)
47 changes: 35 additions & 12 deletions services/adguardhome-sync/README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,42 @@
# AdGuardHome Sync with Tailscale Sidecar Configuration
# AdGuard Home Sync

This Docker Compose configuration sets up **[AdGuardHome Sync](https://github.com/bakito/adguardhome-sync)** with Tailscale as a sidecar container to securely synchronize your AdGuard Home instances over a private Tailscale network. By integrating Tailscale, you ensure that configuration data is transmitted securely between nodes and accessible only to authorized devices in your private network.
[AdGuard Home Sync](https://github.com/bakito/adguardhome-sync) copies the configuration of one AdGuard Home instance to one or more replicas, including filters, rewrites, clients, and DNS settings.

## AdGuardHome Sync
This stack runs AdGuard Home Sync with a Tailscale sidecar, as described in [the standard setup](../../documentation/standard-setup.md).

[AdGuardHome Sync](https://github.com/bakito/adguardhome-sync) is a **lightweight tool for synchronizing configuration between multiple AdGuard Home servers**. It supports syncing DNS settings, clients, rules, rewrites, and more—making it ideal for managing AdGuard Home across multiple networks or locations. Whether you're managing redundant setups or simply keeping home and remote deployments in sync, this tool helps you maintain consistency and saves time.
## At a glance

## Key Features
| Item | Value |
| ------------- | ------------------------------------------------------------------------------- |
| Web interface | None |
| Service port | `8080` on the Tailscale IP address of `adguardhome-sync` (API of the sync tool) |
| Image | `ghcr.io/bakito/adguardhome-sync` |
| Data | None |

* **Multi-Node Syncing** – Keep multiple AdGuard Home instances in sync effortlessly.
* **Granular Configuration** – Choose which parts of the configuration to sync (rules, clients, rewrites, etc.).
* **Push or Pull Modes** – Use a master-push or node-pull strategy to fit your setup.
* **Self-Hosted** – Fully local, no third-party service required.
* **Secure Access with Tailscale** – Safely connect and sync instances across private networks using Tailscale.
## Before you start

## Configuration Overview
Replace the sample values in the `environment` block of `compose.yaml`:

In this setup, the `tailscale-adguardhome-sync` service runs Tailscale, which manages secure networking for the AdGuardHome Sync service. The `adguardhome-sync` container uses the Tailscale network stack via Docker’s `network_mode: service:tailscale` configuration. This ensures that all sync communication is confined to your private Tailscale network, preventing exposure to the public internet.
- **`ORIGIN_URL`, `ORIGIN_USERNAME`, `ORIGIN_PASSWORD`.** The AdGuard Home instance to copy from.
- **`REPLICA1_URL`, `REPLICA1_USERNAME`, `REPLICA1_PASSWORD`.** The instance to copy to.
- **`CRON`.** The schedule. The sample value runs a sync every minute.

To reach an AdGuard Home instance on your Tailnet, see the [DNS section of the standard setup](../../documentation/standard-setup.md#dns).

## Deviations from the standard setup

- **No Tailscale Serve.** The stack has no Serve configuration. The tool only makes outgoing connections to your AdGuard Home instances.
- **Start command.** The stack starts the tool with the `run` command.
- **No data folder.** The tool stores nothing on disk. All settings are in `compose.yaml`.

## First run

Check the log to see whether the sync works:

```bash
docker logs app-adguardhome-sync
```

## Links

- [AdGuard Home Sync documentation and source code](https://github.com/bakito/adguardhome-sync)
Loading
Loading