Setup overview¶
Setup has two stages: create the core, then create apps on the core. Do not start an app until the core reports live.
flowchart TB
A["Prepare host,<br/>tailnet and bucket"] --> B["Render core"]
B --> C["./lk collect"]
C --> D["./lk run"]
D --> E{check status}
E -->|"state: live"| F["Render app<br/>inside repos/"]
E -->|"state: degraded"| G["Read next:<br/>fix and re-dispatch"]
G --> E
F --> H["./lk collect<br/>commit and push"]
H --> I["Dispatch install<br/>on Forgejo Actions"]
I --> J{check verify}
J -->|"verify: green"| K[Operate]
J -->|"verify: red"| I
Before you begin¶
One Linux host¶
| Requirement | Detail |
|---|---|
| Access | Root SSH over the tailnet address |
| Docker | Present, or installable by install setup |
| Network | Outbound HTTPS; free loopback ports 3000, 8080, 8082, 8083 |
| Tailscale | tailscaled running and logged in with its own tagged key |
| Exclusivity | No listener on 0.0.0.0 for a declared port; no second Kamal instance on the host |
The host does not need a public IP, an inbound firewall rule, a checkout, Python or Ruby.
One tailnet¶
- MagicDNS and HTTPS certificates enabled.
- An API key from the Tailscale admin console.
- A tailnet DNS name such as
example.ts.net.
One object store¶
- An S3-compatible bucket with versioning on.
- A credential scoped to read, write, delete and list on that bucket.
- The endpoint, region and bucket name.
Versioning is not optional: retention works by deleting old sets.
A laptop¶
| Tool | Version |
|---|---|
bash |
4 or newer (macOS ships 3.2 — install a current bash) |
git, jq, openssl, curl, ssh |
Current |
ruby |
3.1 or newer (facts are read with ERB) |
python3 |
3.9 or newer (API clients only) |
tailscale |
1.92 or newer |
| Kamal | exactly 2.12.0 |
cookiecutter |
Current |
Run ./lk check preflight --tools to check the laptop, and ./lk check preflight --host to check the host.
Choose your values¶
| Value | Notes |
|---|---|
instance_slug |
Two lowercase words. It becomes every container, Service, path and tag name. Pick it once. |
tailnet_dns |
The tailnet's DNS suffix, for example example.ts.net |
server_tailnet_ip |
The host's 100.x tailnet address |
backup_bucket, backup_endpoint, backup_region |
The object store from above |
notify_channel, notify_url |
Where a red check or failed backup is reported |
admin_emails, forgejo_admin_user, infisical_admin_email |
The first accounts |
The slug is permanent
Renaming an instance changes its containers, Services, host paths and evidence tags. Treat the slug as part of the deployment, not a label you can edit later.
Stage 1: Core¶
cookiecutter /path/to/cookiecutter-lairkit-core
cd lairkit-core-<slug>
./lk collect
./lk run
./lk check status
Full details: Set up a core instance.
Stage 2: Apps¶
cd lairkit-core-<slug>
cookiecutter /path/to/cookiecutter-lairkit-iac-<app> -o repos \
instance_slug=<two-words> \
exposure=tailnet-only \
server_tailnet_ip=100.64.0.10
cd repos/lairkit-iac-<app>-<two-words>
./lk collect
git add -A && git commit -m "initial render" && git push -u origin main
Then dispatch the install workflow on the app repository in Forgejo.
Full details: Set up apps and each app's notes in App operations.
What "done" looks like¶
A core is done when:
An app is done when:
./lk check status # live
./lk check verify # all probes green
./lk check backup # newest set inside the RPO
When a step fails¶
Every action returns one of five codes:
| Code | Do this |
|---|---|
0 |
Continue |
10 |
Nothing to change; continue |
11 |
Read the failing line and its next: hint |
2 |
Fix the refusal — dirty tree, missing confirmation, missing credential |
1 |
The change ran and failed; read the evidence tag and the failing component |
Do not re-run install setup to repair a crash-looping accessory. The rendered runbooks give the repair command, usually ./lk change upgrade --component <name>.