Skip to content

Commit 81bdc70

Browse files
authored
Docs: update AGENTS, CONTRIBUTING, and the templates (#372)
* Docs: update AGENTS, CONTRIBUTING, and the templates * Template: keep two database health check rules CONTRIBUTING.md now points to the template comments for the database case. Two rules from the removed paragraph were missing there: a database image's own HEALTHCHECK comes first, and a service that depends on the database waits with condition: service_healthy. * Docs: say how to validate the template The documented validation command fails on the template, because SERVICE and IMAGE_URL are empty there. AGENTS.md and CONTRIBUTING.md now give the command with dummy values, as CI uses. * Template: give a tagged image as the example CONTRIBUTING.md asks for an explicit image tag, but the example in the template had none and misspelled the image name. * Docs: scope the rules to new and changed parts, name rumdl without a version number The rules apply to new stacks and to the parts of an existing stack that a change touches, so existing stacks that differ are not violations to fix at once. The lint instructions read the rumdl version from linting.yml and offer the release binary next to uvx, so a version bump needs no doc edit. * Docs: name the upstream branch for forks, and the .gitignore exception git diff --check origin/main compares with the wrong branch in a fork. A tracked file inside a data folder needs its own exception in .gitignore, because the folder rules ignore it.
1 parent ca53d62 commit 81bdc70

5 files changed

Lines changed: 177 additions & 65 deletions

File tree

‎.github/PULL_REQUEST_TEMPLATE.md‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Servicename: context
2-
<!-- Examples: Dockhand: new service | Changedetection: fix port mapping -->
2+
<!-- Title format: "Service: what changes", as in CONTRIBUTING.md. Examples: Dockhand: new service | Changedetection: fix port mapping -->
33

44
## Description
55
<!-- Briefly describe what changed. -->
@@ -17,7 +17,9 @@
1717
- [ ] I have performed a self-review of my code and followed the [templates](https://github.com/tailscale-dev/ScaleTail/tree/main/templates/service-template) structure.
1818
- [ ] I have added verification that the stack works as expected.
1919
- [ ] I have updated necessary documentation (e.g. frontpage [README.md](https://github.com/tailscale-dev/ScaleTail/blob/main/README.md) ).
20-
- [ ] I have selected the correct label(s) for this PR.
20+
- [ ] I ran Compose validation with dummy values for required variables.
21+
- [ ] I ran rumdl on the whole repository.
22+
- [ ] I committed no working credential.
2123

2224
## Additional Context
2325
<!-- Any extra info for reviewers, such as gotchas, special requirements, devices, or dependencies. Use "None" if not applicable. -->

‎AGENTS.md‎

Lines changed: 48 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,62 @@
11
# Agent guidance
22

3-
Follow [CONTRIBUTING.md](CONTRIBUTING.md) and the current service template.
3+
ScaleTail is a collection of Docker Compose stacks. Each directory in `services/` is one stack, which usually runs a self-hosted application behind a Tailscale sidecar container.
4+
5+
## Where the rules live
6+
7+
- [CONTRIBUTING.md](CONTRIBUTING.md): how to add, update, verify, and submit a service. Follow it.
8+
- [templates/service-template/](templates/service-template/): the starting point for every service. Its comments explain each setting.
9+
- [documentation/standard-setup.md](documentation/standard-setup.md): the shared setup. A service README lists only what differs, under "Deviations from the standard setup".
10+
- [.github/workflows/](.github/workflows/): the Compose validation and Markdown lint that CI runs.
411

512
## Before you edit
613

7-
- Inspect the target service and its README.
8-
- Confirm ports, commands, volumes, and variables in the image's upstream docs.
9-
- Preserve service-specific exceptions that still apply.
14+
- Read the service's `compose.yaml`, `.env`, and README, including "Deviations from the standard setup".
15+
- Confirm ports, commands, volumes, variables, and health checks in the image's upstream docs.
16+
- Preserve documented exceptions. Some stacks use other Compose service keys or container names, and other containers or `.env` values refer to those names.
1017
- Keep changes focused on the requested service or documentation.
11-
- Never commit working auth keys, passwords, tokens, or other credentials.
18+
- Apply the rules to new stacks and to the parts of an existing stack that you change. Existing stacks are brought in line over time, so do not fix the rest of a stack unless that is the task.
19+
20+
## Files that change together
21+
22+
- Add, rename, or remove a service: the `services/<name>/` directory and its row in the root `README.md`. Insert a new row in alphabetical order by the bold name, and leave the other rows alone.
23+
- Make existing users act, such as with a new required secret, a moved volume, or a changed `SERVICE`: add an "Upgrading" section to the service README. `SERVICE` also names the Tailnet device, the containers, and most data folders.
24+
- Change the template: also update `documentation/standard-setup.md` and `CONTRIBUTING.md` where they describe the same setting. Validate it from `templates/service-template/` with `SERVICE=dummy IMAGE_URL=dummy docker compose config --quiet`, because the template leaves both values empty.
25+
26+
## Secrets and local data
27+
28+
- Every `.env` file is tracked and published. Never commit an auth key, password, token, or other credential.
29+
- Leave each secret empty in `.env`. When the stack cannot start without it, read it in `compose.yaml` as `${VAR:?Set VAR in .env}`, and add a `# Required:` comment above it in `.env` that says what to set, such as how to generate it. Keep `TS_AUTHKEY` optional, because only the first start needs it.
30+
- Start test stacks from a copy outside the repository, such as `cp -R services/<name> /tmp/<name>`, and put real values only in the copy. A running stack writes `./config`, `./ts/state` (with the device key), and its data folders next to `compose.yaml`.
31+
- Before you commit, run `git status` and stage only the files you meant to change.
1232

1333
## Verification
1434

15-
Run `docker compose config --quiet` from each changed service directory. When
16-
possible, start the stack and test its main function through the Tailnet.
35+
From each changed service directory, validate Compose. The command gives each required variable a dummy value, as CI does, so you do not edit `.env`:
1736

18-
From the repository root, lint the changed Markdown files. Replace the example paths below as needed:
37+
```sh
38+
env $(grep -ohE '\$\{[A-Za-z0-9_]+:\?' compose.yaml .env | sed -E 's/^\$\{//; s/:\?$/=dummy/' | sort -u) docker compose config --quiet
39+
```
40+
41+
It must exit with status 0. CI fails on "variable is not set" warnings.
42+
43+
From the repository root, lint all Markdown with the rumdl version that `.github/workflows/linting.yml` pins in its `version:` input, and check your changes for whitespace errors:
1944

2045
```sh
21-
rumdl check --config .markdownlint.yml AGENTS.md CONTRIBUTING.md
22-
git diff --check
46+
version=$(sed -nE 's/.*version: "([0-9.]+)".*/\1/p' .github/workflows/linting.yml)
47+
uvx "rumdl@$version" check --config .markdownlint.yml .
48+
git diff --check origin/main
2349
```
2450

25-
Report the checks you ran. State any checks that credentials, hardware,
26-
permissions, or unavailable tools prevented.
51+
Without `uv`, download that version from the [rumdl releases](https://github.com/rvben/rumdl/releases) and run `rumdl check --config .markdownlint.yml .`.
52+
53+
Lint the whole tree, because renaming a service breaks a link in the root `README.md`. `git diff --check origin/main` also covers staged and committed changes, but not untracked files, so stage new files first. In a fork, compare with the `main` branch of this repository instead, for example `upstream/main`.
54+
55+
When you have an auth key, start the copied stack and test its main function through the Tailnet. Run each health check command in its container with `docker exec`.
56+
57+
Report the checks you ran. State any checks that credentials, hardware, permissions, or unavailable tools prevented.
58+
59+
## Pull requests
60+
61+
- Title the pull request `Service: what changes`, such as `Umami: new service`. For several services, list them or use `All services:`. For other areas, use `CI:`, `Docs:`, or `Template:`.
62+
- Fill in the [pull request template](.github/PULL_REQUEST_TEMPLATE.md). Maintainers apply the labels.

‎CONTRIBUTING.md‎

Lines changed: 110 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
Thanks for helping improve these Tailscale sidecar examples.
44

5+
The rules on this page apply to new stacks and to the parts of an existing
6+
stack that you change. Existing stacks are brought in line over time, so you
7+
do not have to fix the rest of a stack that you touch.
8+
59
## Add a service
610

711
1. Copy the service template from the repository root. Replace `my-service` with your service name:
@@ -12,33 +16,64 @@ Thanks for helping improve these Tailscale sidecar examples.
1216

1317
This command includes the hidden `.env` file. Use a lowercase directory name.
1418

15-
2. Update `.env` with safe example values.
19+
2. Update `.env`.
20+
21+
Set `SERVICE`, usually to the directory name, and set `IMAGE_URL`,
22+
`SERVICEPORT`, and the application variables. Define every variable that
23+
`compose.yaml` uses, unless it has a default such as `${VAR:-value}`. Put
24+
comments on their own line: `VAR= # note` sets `VAR` to `# note`.
25+
26+
`.env` is published with the repository. Never commit a working auth key,
27+
password, token, or other credential. Leave each secret empty. When the stack
28+
cannot start without it, require it in `compose.yaml`:
29+
30+
```yaml
31+
- DB_PASSWORD=${DB_PASSWORD:?Set DB_PASSWORD in .env}
32+
```
1633
17-
Set `SERVICE`, `IMAGE_URL`, `SERVICEPORT`, and the application variables.
18-
Never commit a working auth key, password, token, or other credential.
34+
Compose then stops with an error until the user sets a value. Above the
35+
empty variable in `.env`, add a comment that starts with `# Required:` and
36+
says what to set, such as
37+
`# Required: signs the sessions. Generate it with: openssl rand -hex 32`.
38+
Keep `TS_AUTHKEY` and optional secrets without `:?`.
1939

2040
3. Adapt `compose.yaml`.
2141

2242
- Keep the Compose service keys `tailscale` and `application`.
23-
- Name the containers `tailscale-${SERVICE}` and `app-${SERVICE}`.
43+
- Name the containers `tailscale-${SERVICE}` and `app-${SERVICE}`, unless the
44+
stack documents a deviation under "Deviations from the standard setup".
2445
- Keep `network_mode: service:tailscale` on the application.
2546
- Keep the application's health-based dependency on `tailscale`.
2647
- Add all required persistent volumes.
2748
- Add required devices and capabilities explicitly.
49+
- When the stack needs another Compose service key or container name, more
50+
than one application container, or none, list the difference and the
51+
reason under "Deviations from the standard setup". Prefer
52+
`app-${SERVICE}-<role>` for extra containers, such as `app-${SERVICE}-db`.
53+
- Pass `PUID` and `PGID` only when the image reads them, such as LinuxServer.io
54+
images. Keep `TZ`, as the template does.
55+
- Write the image tag explicitly and use the tag upstream recommends. Pin
56+
databases, caches, and search engines to a major or major.minor tag. When
57+
you pin an exact version, say why in a comment.
58+
- Keep application data under `./${SERVICE}-data/`, so that `.gitignore`
59+
covers it. A file that the repository must track inside such a folder
60+
needs its own exception in `.gitignore`, as for Donetick and Dozzle.
2861

2962
4. Set the Serve proxy to the application's internal port.
3063

31-
The Serve JSON does not read `SERVICEPORT` from `.env`. Keep runtime variables
32-
escaped, such as `$${TS_CERT_DOMAIN}`.
64+
Hard-code the port in the Serve JSON. Do not use `${SERVICEPORT}`: it is
65+
meant for the optional LAN `ports` block and can differ from the port that
66+
Serve must proxy to. Keep runtime variables escaped, such as
67+
`$${TS_CERT_DOMAIN}`.
3368

3469
Keep the `ports` block commented for Tailnet-only access. Document any LAN
35-
port you expose. Remove the Serve configuration when the service does not use
36-
Tailscale Serve.
70+
port you expose. When nothing in the stack uses Tailscale Serve, remove the
71+
Serve configuration: the `TS_SERVE_CONFIG` line, the `configs` entry of the
72+
`tailscale` service, and the top-level `configs` block.
3773

3874
5. Configure the application's health check.
3975

40-
Use the first option that the image supports. The template lists the same
41-
options in the same order.
76+
Use the first option that the image supports.
4277

4378
1. When the image defines its own `HEALTHCHECK`, omit the block and add a
4479
`# Healthcheck: defined by the image (...)` comment that names the
@@ -54,42 +89,37 @@ Thanks for helping improve these Tailscale sidecar examples.
5489
is possible. Omit the block and add a `# Healthcheck: none possible ...`
5590
comment that names the reason.
5691

57-
Run the command inside the running container before you commit it. Avoid
58-
`pgrep -f ${SERVICE}`, which breaks when `SERVICE` is renamed.
59-
60-
A container that runs once and exits needs no check. Omit the block and
61-
add a `# Healthcheck: none needed ...` comment that names the reason.
62-
63-
For a database, connect over TCP, for example with
64-
`pg_isready -h 127.0.0.1` or `mariadb-admin ping -h 127.0.0.1`. During the
65-
first start, the image runs a temporary server that accepts only socket
66-
connections, so a socket check reports ready too early. When another
67-
service depends on the database, wait with `condition: service_healthy`.
68-
A database image's own `HEALTHCHECK` still comes first. When that check
69-
connects over the socket, say so in the comment.
70-
71-
Keep the Tailscale health check.
92+
Use the comment text from the template's `compose.yaml`, which covers the
93+
same options, the database case, and the no-check cases. Run the command
94+
inside the running container before you commit it. Keep the Tailscale health
95+
check.
7296

7397
6. Complete the service README.
7498

7599
Every service README uses the headings of the template, in the same order.
76-
Do not rename them and do not add others, so that every service reads the
77-
same way. Replace the placeholders in capitals.
100+
Do not rename them, and add no headings other than the optional sections
101+
below, so that every service reads the same way. Replace the placeholders in
102+
capitals.
78103

79-
- **Introduction.** Say what the service does in one or two sentences and
80-
link to the upstream project. Leave feature lists to upstream.
104+
- **Introduction.** The paragraph under the title. Say what the service does
105+
in one or two sentences and link to the upstream project. Leave feature
106+
lists to upstream.
81107
- **At a glance.** Give the Tailnet address, the port that Tailscale Serve
82108
forwards to, the image, and the data paths on the host. Add a row for
83-
each further port that users connect to, such as DNS or SMTP.
109+
each further port that users connect to, such as DNS or SMTP. Write `None`
110+
for the web interface when there is none, and add an `API` row when Serve
111+
publishes an API.
84112
- **Before you start.** List only what the Quick Start does not cover:
85113
values that must change in `.env`, secrets to generate, folders to
86-
create, and required host groups or devices.
114+
create, and required host groups or devices. List every required secret
115+
and say that Compose stops with an error until it is set.
87116
- **Deviations from the standard setup.** List every difference from
88117
[the standard setup](documentation/standard-setup.md) and give the
89118
reason. Examples are extra containers, published host ports, a changed
90119
or removed Serve configuration, DNS settings, and added capabilities.
91120
- **First run.** Describe what the user does after the first start, such
92-
as creating the first account or finding a generated password.
121+
as creating the first account or finding a generated password. When there
122+
is nothing to do, write `Nothing to set up. Open the web interface.`
93123
- **Links.** Link to the upstream documentation and source code.
94124

95125
When a section has nothing to report, keep the heading and the sentence
@@ -101,15 +131,17 @@ Thanks for helping improve these Tailscale sidecar examples.
101131

102132
- **Configuration.** Optional settings that users commonly change.
103133
- **Troubleshooting.** Known errors and their solutions.
104-
- **Upgrading.** Steps for users of an older version of the stack.
134+
- **Upgrading.** Required when a change makes existing users act, such as a
135+
new required secret, a moved data folder, or a changed `SERVICE`. Say what
136+
to do, as in [FossFLOW](services/fossflow/README.md#upgrading).
105137

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

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

112-
Keep the entries in that category alphabetized.
144+
Insert the row in alphabetical order of the bold name, ignoring case.
113145

114146
## Update a service
115147

@@ -119,20 +151,55 @@ Thanks for helping improve these Tailscale sidecar examples.
119151
- Use `${VARIABLE}` for Compose interpolation, not `$(VARIABLE)`.
120152
- Update the service README when ports, paths, setup, or behavior change.
121153
- Update the root service list when you add, remove, or rename a service.
154+
- Changing `SERVICE` renames the Tailnet device and its address, the
155+
containers, and the `./${SERVICE}-data` folder. Treat it like a volume change
156+
and add an "Upgrading" section.
157+
- Do not rename existing containers or service keys. Other containers and
158+
`.env` values may refer to them.
122159

123160
Preserve valid service-specific exceptions.
124161

125162
## Verify your change
126163

127-
Run Compose validation from each changed service directory:
164+
Run Compose validation from each changed service directory. Stacks that
165+
require your own secrets stop with "required variable ... is missing a value".
166+
Give those variables a dummy value on the command line, as CI does, instead of
167+
editing `.env`:
128168

129169
```sh
130-
docker compose config --quiet
170+
env $(grep -ohE '\$\{[A-Za-z0-9_]+:\?' compose.yaml .env | sed -E 's/^\$\{//; s/:\?$/=dummy/' | sort -u) docker compose config --quiet
131171
```
132172

133-
This command does not prove that the application works.
173+
The command must exit with status 0 and add no "variable is not set"
174+
warnings. It does not prove that the application works.
175+
176+
The template leaves `SERVICE` and `IMAGE_URL` empty. To validate it, run
177+
`SERVICE=dummy IMAGE_URL=dummy docker compose config --quiet` from
178+
`templates/service-template/`.
179+
180+
From the repository root, lint all Markdown with the rumdl version that
181+
`.github/workflows/linting.yml` pins in its `version:` input:
182+
183+
```sh
184+
version=$(sed -nE 's/.*version: "([0-9.]+)".*/\1/p' .github/workflows/linting.yml)
185+
uvx "rumdl@$version" check --config .markdownlint.yml .
186+
```
187+
188+
Without `uv`, download that version from the
189+
[rumdl releases](https://github.com/rvben/rumdl/releases) and run
190+
`rumdl check --config .markdownlint.yml .`.
191+
192+
After you stage new files, check whitespace with `git diff --check origin/main`.
193+
In a fork, compare with the `main` branch of this repository instead, for
194+
example `upstream/main`.
195+
196+
When possible, start the stack from a copy outside the repository, such as
197+
`cp -R services/my-service /tmp/my-service`, and put real values only in the
198+
copy. A running stack writes `./config`, `./ts/state` (with the Tailscale
199+
device key), and its data folders next to `compose.yaml`. Before you commit,
200+
run `git status` and stage only the files you changed on purpose.
134201

135-
When possible, start the stack and confirm:
202+
Then confirm:
136203

137204
- Tailscale becomes healthy and joins the Tailnet.
138205
- The application starts and is reachable through the Tailnet.
@@ -141,5 +208,8 @@ When possible, start the stack and confirm:
141208

142209
## Submit a pull request
143210

144-
Follow the pull request template. Report the checks you ran and any checks you
145-
could not run.
211+
Title the pull request `Service: what changes`, such as `Umami: new service`.
212+
For several services, list them or use `All services:`. For other areas, use
213+
`CI:`, `Docs:`, or `Template:`. Fill in the pull request template, and report
214+
the checks you ran and any checks you could not run. Maintainers apply the
215+
labels.

‎templates/service-template/.env‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,27 @@
1-
#version=1.1
21
#URL=https://github.com/tailscale-dev/ScaleTail
3-
#COMPOSE_PROJECT_NAME= # Optional: only use when running multiple deployments on the same infrastructure.
2+
# To run a second copy, copy the folder under another name and change SERVICE.
43

54
# Service Configuration
65
# Service name (e.g., adguard). Used as hostname in Tailscale and for container naming (app-${SERVICE}).
76
SERVICE=
8-
# Docker image URL from container registry (e.g., adguard/adguard-home).
7+
# Docker image URL from container registry (e.g., adguard/adguardhome:latest).
98
IMAGE_URL=
109

1110
# Network Configuration
1211
# Port to expose to local network. Uncomment the "ports:" section in compose.yaml to enable.
1312
SERVICEPORT=
14-
DNS_SERVER=9.9.9.9 # Preferred DNS server for Tailscale. Uncomment the "dns:" section in compose.yaml to enable.
13+
# Preferred DNS server for Tailscale. Uncomment the "dns:" section in compose.yaml to enable.
14+
DNS_SERVER=9.9.9.9
1515

1616
# Tailscale Configuration
17-
# Auth key from https://tailscale.com/admin/authkeys. See: https://tailscale.com/kb/1085/auth-keys#generate-an-auth-key for instructions.
17+
# Auth key from https://console.tailscale.com/admin/settings/keys. See: https://tailscale.com/kb/1085/auth-keys#generate-an-auth-key for instructions.
1818
TS_AUTHKEY=
1919

2020
# Optional Service variables
2121
# PUID=1000
2222

23-
# Time Zone setting for containers
24-
TZ=Europe/Amsterdam # See: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
23+
# Time Zone setting for containers. See: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
24+
TZ=Europe/Amsterdam
2525

2626
# Any Container environment variables are declared below. See https://docs.docker.com/compose/how-tos/environment-variables/
2727

0 commit comments

Comments
 (0)