Skip to content

Add a deployable server stack with TLS, and strip local panels from the public board - #5

Merged
tschm merged 1 commit into
mainfrom
add/server-deployment
Aug 29, 2026
Merged

Add a deployable server stack with TLS, and strip local panels from the public board#5
tschm merged 1 commit into
mainfrom
add/server-deployment

Conversation

@tschm

@tschm tschm commented Aug 29, 2026

Copy link
Copy Markdown
Member

Everything needed to run the board world-readable on an always-on host. The laptop stack cannot be that: Docker pauses when the Mac sleeps, and there are no working copies on a server anyway.

What's here

  • docker-compose.server.yml — standalone, not an overlay. No bind mount and an empty JQ_REPO_ROOT so local scanning is skipped; JQ_PUBLIC_ONLY forced on; only Grafana publishes a port; anonymous access off, public dashboards on.

  • docker-compose.tls.yml — adds Caddy, stops publishing Grafana entirely, and obtains/renews a certificate by itself. With it, only Caddy is exposed, so the admin login never crosses the internet in clear.

  • scripts/bootstrap-server.sh — refuses to come up half-configured, because a half-configured public board is worse than none. Every guard is tested:

    error  .env not found
    error  ACME_EMAIL is not set in .env
    error  GF_ADMIN_PASSWORD is shorter than 16 characters - this host is on the internet
    error  nope.jqr.ae does not resolve - add the A record first
    warn   jqr.ae points at X but this host is Y - certificate issuance will fail
    error  GITHUB_TOKEN rejected by the GitHub API (HTTP 401)
    

    Only past all of those does it start the stack and run check-public-safe.sh, failing loudly rather than declaring success.

  • Empty JQ_REPO_ROOT is now a clean no-op rather than an error logged every minute, with a test.

The public board no longer describes anyone's laptop

make-public-dashboard.py drops every panel sourced from jq_local_* — dirty files, checked-out branch names, fetch ages. Not secret when the repos are public, but it is someone's working state, it is meaningless to anyone else, and it is empty on a server. Mixed panels keep their non-local series, links into dropped panels are pruned, and the layout is repacked preserving bands so a row of tiles doesn't become a column.

Verified

Run from a clean git archive of this branch: all five compose combinations validate, 2 dashboards valid, 21 tests pass. The server stack was also built and run for real earlier — 719 series, 0 local, 23 repos, 0 private, anonymous reach 200 on the public dashboard and 401 on /api/search, /api/ds/query and the datasource proxy.

CI now validates the server stack and the TLS overlay alongside the others.

Why not Fly.io

Recorded in the README so nobody repeats it: flyctl v0.4.95 does not implement the [build.compose] its docs describe — neither fly deploy nor fly launch detects a compose file, and there is no --compose flag. Two further gotchas found before hitting that wall are written down in case it ever ships.

🤖 Generated with Claude Code

…he public board

Everything needed to run the board world-readable on an always-on host, which
the laptop stack cannot be: Docker pauses when the Mac sleeps, and there are no
working copies on a server anyway.

- docker-compose.server.yml is standalone, not an overlay. No bind mount and an
  empty JQ_REPO_ROOT so local scanning is skipped; JQ_PUBLIC_ONLY forced on;
  only Grafana publishes a port; anonymous access off and public dashboards on.
- docker-compose.tls.yml adds Caddy, stops publishing Grafana entirely, and
  obtains and renews a certificate by itself. With it, only Caddy is exposed -
  the admin login never crosses the internet in clear.
- scripts/bootstrap-server.sh refuses to come up half-configured, because a
  half-configured public board is worse than none. It checks docker, all four
  settings, a 16-character minimum on the admin password, that the domain
  resolves *here* (the ACME challenge needs it), and that the GitHub token is
  accepted - then starts the stack and runs the preflight, failing loudly rather
  than declaring success.
- An empty JQ_REPO_ROOT is now a clean no-op instead of an error logged every
  minute, with a test.
- make-public-dashboard.py drops every panel sourced from jq_local_*. Those
  describe one particular machine - its dirty files, its checked-out branch
  names - which is not secret when the repos are public, but is someone's
  working state, is meaningless to anyone else, and is empty on a server.
  Mixed panels keep their non-local series; links into dropped panels are
  pruned; the layout is repacked preserving bands so a row of tiles does not
  become a column.

CI now validates the server stack and the TLS overlay alongside the others.

The README records why Fly.io was abandoned: flyctl v0.4.95 does not implement
the [build.compose] its docs describe, and neither fly deploy nor fly launch
detects a compose file at all.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@tschm
tschm merged commit 9152c32 into main Aug 29, 2026
4 checks passed
@tschm
tschm deleted the add/server-deployment branch August 29, 2026 11:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant