Files
MrAlders0n f2728b059b docs: reorganise into a deploy and operate front door (#12)
* docs: move design and API contract into docs/

* docs: add releases and versioning page

* docs: add configuration reference for server and web

* docs: add reverse proxy page

* docs: add upgrading page

* docs: add operations page and move backup and profiling docs

* docs: add getting packets in and security pages

* docs: add troubleshooting page

* docs(api): fold admin, activity, route and summary notes into the contract

* docs(deploy): add type 2 split deployment, finish the beacon rename, ignore secrets

* docs: rewrite README as the front door and share the contributor workflow

* docs: remove em dashes from prose and config comments

* docs: apply review fixes to type 2, upgrading, troubleshooting and first-run notes

* docs: fix upgrade order, restore steps, type 2 setup and contract details from review

* docs: keep the gitignore to deployment secrets and runtime state
2026-10-02 21:42:13 -04:00

116 lines
4.9 KiB
Markdown

# Day-to-day operations
Everything here assumes the all-in-one Docker stack. The commands translate to the split
deployment by running them on the server host.
## Checking it is healthy
Beacon has no health endpoint yet. Use these instead:
- `docker compose ps` shows every container `Up`. `db` and `redis` have health checks; `app`
waits for both before it starts.
- `curl -s https://<your-domain>/api/v1/brokers` lists each MQTT broker with its connection
status. A broker that never connects is a URL or credentials problem; see
[Getting packets in](getting-packets-in.md).
- Open `wss://<your-domain>/ws` in a WebSocket client. The first frame is `hello`.
- `docker compose logs -f app` shows a `connected` line per broker. Individual packets are
not logged at the default `info` level, so a quiet log after that is normal; use the
observers endpoint or the web app to see traffic.
## Logs
Logs go to stderr and Docker collects them. Docker's default `json-file` driver does not
rotate, so on a long-running host set a cap, either in `/etc/docker/daemon.json` for every
container or per service in `docker-compose.yml`:
```yaml
logging:
driver: json-file
options: { max-size: "50m", max-file: "5" }
```
Set `log.level` (`debug`, `info`,
`warn`, `error`; default `info`) and `log.format` (`text` or `json`) in `config.yaml`, or
override them with `LOG_LEVEL` and `LOG_FORMAT`. Invalid values stop startup.
Every record has a `component`. Ingest records add the broker name; HTTP completion records
add the validated client address, route, status and duration. Query strings and WebSocket
hello payloads are never logged. Expected ingest skips and routine WebSocket lifecycle events
are at debug level.
```bash
docker compose logs -f app # the API and ingest
docker compose logs -f caddy # the proxy
```
Caddy also writes a JSON access log to `data/Caddy/logs/access.json`, which the fail2ban rules
in [`app_config/fail2ban/`](../app_config/fail2ban/README.md) read.
## Where the data lives
Everything persistent is under `data/` next to `docker-compose.yml`:
- `data/postgres/` is the database. Deleting it deletes all history.
- `data/redis/` is the cache. It can be removed while the stack is down; Beacon rebuilds it.
- `data/Caddy/data/` holds TLS certificates. Deleting it means a new certificate request on
the next start, which counts against Let's Encrypt rate limits.
- `data/app/config.yaml` and `.env` are your configuration. Keep copies of both somewhere
else; no backup below includes `.env`.
## Retention
Everything below is a duration in `config.yaml`. Shorten them if disk is tight, lengthen them
if you want more history.
| Setting | Default | What it covers |
|---|---|---|
| `packets.retention` | `168h` (7 days) | Packets, observations and channel messages |
| `analytics.rollup_retention` | `2160h` (90 days) | Hourly stats rollups. Also the longest window the stats endpoints accept |
| `telemetry.retention` | `744h` (31 days) | Observer telemetry snapshots |
| `routes.retention` | `336h` (14 days) | Known routes, counted from when a route was last seen |
| `nodes.delete_after` | `720h` (30 days) | Nodes not heard from |
| `observers.delete_after` | off | Observers not heard from. Opt in by setting a duration |
Historical stats read the hourly rollups, not raw packets. Each UTC hour is rolled about 95
minutes after it closes, and packet cleanup holds back up to 24 hours of raw rows until their
hours are rolled, so raw data lives a little longer than `packets.retention` says. An hour
whose raw rows were deleted first is reported as partial, with no values.
## Backing up
Two options.
**A plain `pg_dump`** from the host, which is enough for most operators:
```bash
docker compose exec -T db pg_dump -U beacon -d beacon -Fc > beacon-$(date +%F).dump
```
Keep `data/app/config.yaml` and `.env` with it. The dump holds message data and anything you
have decrypted, so treat it as private.
**The admin backup endpoint** produces an archive of the database and the saved
`config.yaml`, with a manifest and checksums, and a command-line tool that verifies it. It is
off by default and needs the admin key. See [Backup and export](backup-export.md).
## Restoring
Restore into an empty database, never over live data. With the stack running and the app
stopped, drop and recreate the database, load the dump, and only then start the app:
```bash
docker compose stop app
docker compose exec -T db psql -U beacon -d postgres -c 'DROP DATABASE beacon;' -c 'CREATE DATABASE beacon;' \
&& docker compose exec -T db pg_restore -U beacon -d beacon < beacon-YYYY-MM-DD.dump \
&& docker compose start app
```
If `pg_restore` reports errors the app is not started; read them before trying again.
A 1.x dump cannot be restored into 2.0.0; the server refuses the old schema. See
[Upgrading](upgrading.md#upgrading-to-200-from-1x).
## CPU profiling
Bounded CPU captures on a running instance are described in [Profiling](profiling.md).