Publishing piqueld.dev
piqueld.dev is served by a piqueld instance, as two applications:
piqueld-devserves the marketing site at https://piqueld.dev and this book at https://docs.piqueld.dev. Each preview serves a branch’s site and book on the tailnet.piqueld-observabilityreceives the instance’s own telemetry, and shows it in Grafana atgrafana.piqueld.dev, on the instance’s tailnet.
Every push to main deploys both. This page sets them up once, on the
instance that serves them, then shows how to preview a change.
deploy/piqueld-dev.toml
and
deploy/piqueld-observability.toml
are their manifests. piqueld reads each from main on each deployment, with
the files its services build and mount, from the same commit:
deploy/siteholds theDockerfileof thesiteservice. It servesapps/site, static pages with no build step, with static-web-server on port 8080, as an unprivileged user.deploy/docsholds theDockerfileof thedocsservice. It builds the book with the mdBook thatflake.lockpins, throughnix develop .#docs, the shelljust docsand CI use. It serves the pages with static-web-server too.deploy/observabilityholds the configuration of the telemetry stack: an OpenTelemetry Collector receiving the daemon’s telemetry, Prometheus keeping metrics for 15 days, Tempo and Loki keeping traces and logs for 7, and Grafana. Grafana’s data sources link logs and traces both ways, and metric exemplars to traces. Its dashboards cover the daemon’s health, deployments and builds, services, and logs.
The telemetry stack is an application of its own because previews are never granted telemetry: in a preview of one application with both, the collector couldn’t start. The user guide’s observability stack is a smaller version of this stack, for any instance.
To add another site, add its service, route and hostname variables to
deploy/piqueld-dev.toml in a pull request.
Before you start
These steps set up an instance that doesn’t serve piqueld.dev yet. If the instance already serves it, such as from other applications, migrate that deployment to these two applications instead: these steps would create new ones, which can’t deploy while the others hold their hostnames.
- The instance serves public routes through
HTTP ingress, and has a
webhook hostname whose DNS is public, so
GitHub can report pushes. It also serves
private routes on its tailnet, with
a DNS-01 provider for
piqueld.dev. - The host is
x86_64-linux, the only Linux system the flake’sdocsshell supports, and can reach GitHub andcache.nixos.orgwhile it builds. - You can create applications on the instance, change and deploy them, and
run commands in them:
apps:create,apps:write,apps:deployandapps:exec. Grantingpiqueld-observabilitythe daemon’s telemetry also needstelemetry:grant, which onlyadminholds by default; see permissions. - You can change the DNS of
piqueld.devand the settings of thesourceorigin-dev/piqueldrepository on GitHub.
Run the piquelctl commands on this page from a checkout of main, on the
host or on any machine where piquelctl is signed in to the instance.
Set it up
-
Point the hostnames at the instance:
piqueld.dev,www.piqueld.devanddocs.piqueld.dev, like any public route: anArecord to the host’s public address, a CNAME to the tunnel (flattened at the zone’s apex) with a Cloudflare Tunnel, or nothing when piqueld manages the zone’s records.grafana.piqueld.devand*.preview.piqueld.dev, like any private route:AandAAAArecords to the apps node’s tailnet addresses, or nothing when piqueld manages the zone’s records. The wildcard covers every preview’s hostnames.
-
Check what saving the manifests would do:
piquelctl app plan --file deploy/piqueld-dev.toml piquelctl app plan --file deploy/piqueld-observability.tomlThe first plan creates the
piqueld-devapplication, with itssiteanddocsservices, its routes onpiqueld.devanddocs.piqueld.dev, and the redirect fromwww.piqueld.dev. The second createspiqueld-observability, with itscollector,prometheus,tempo,lokiandgrafanaservices, and its route ongrafana.piqueld.dev. -
Save the websites’ manifest and deploy it:
piquelctl app apply --file deploy/piqueld-dev.toml piquelctl --timeout 10m app deploy piqueld-devapp applyandapp deployask you to confirm.app deploythen waits until the deployment finishes. The first build downloads the Nix image and the documentation toolchain, and can take several minutes. Later builds reuse them, and only rebuild the book. -
Save the telemetry stack’s manifest, grant its environment the daemon’s telemetry, and deploy it:
piquelctl app apply --file deploy/piqueld-observability.toml piquelctl env telemetry piqueld-observability production on piquelctl app deploy piqueld-observabilityGrant the telemetry first: without it, the collector can’t start, and the deployment fails.
-
Generate the secret of each application’s webhook:
piquelctl app repository webhook rotate piqueld-dev piquelctl app repository webhook rotate piqueld-observabilityEach prints its application’s payload URL and secret. piqueld never shows a secret again.
-
In GitHub, open the repository’s Settings, then Webhooks, and select Add webhook. Set Payload URL and Secret to the values
piqueld-devprinted, Content type toapplication/json, and the events to Just the push event. Then select Add webhook. Add a second webhook the same way, with the valuespiqueld-observabilityprinted.
From now on, change the sites and the stack through pull requests to main,
including their manifests: each push to main deploys the manifests, the
book and the stack’s configuration of that commit. Services whose
configuration didn’t change keep running. Applying a file again is safe,
since it is the same manifest the repository holds.
Repository-backed manifests
and deploying on push
describe how this works.
Check that it works
The site serves the marketing pages and the book:
curl --fail --head https://piqueld.dev/
curl --fail --head https://docs.piqueld.dev/dev/publishing.html
piquelctl status shows the telemetry receiver connected. Open
https://grafana.piqueld.dev from the tailnet, and sign in as admin with the
generated password:
piquelctl app exec piqueld-observability grafana -- cat /run/secrets/grafana-admin-password
Grafana sets it when it first starts, so regenerating the secret doesn’t change it; change it in Grafana instead. Grafana’s piqueld folder holds the dashboards.
After the next push to main, check that both deployed:
piquelctl env show piqueld-dev production
piquelctl env show piqueld-observability production
Each Sync line says following, with the commit of that push. In GitHub,
each webhook’s Recent Deliveries shows each push, with piqueld’s response
204.
Preview a branch
A preview of piqueld-dev
serves a branch’s site and book, so you can read a change before it reaches
main. Its routes are private, on the tailnet. With the slug piqueld gives
the preview, such as piqueld-dev-feat-x-3fa2c1, the site is on
https://<slug>.preview.piqueld.dev and the book on
https://docs-<slug>.preview.piqueld.dev.
To preview your worktree without pushing it, uncommitted changes included, run this from the worktree:
piquelctl --timeout 10m preview deploy piqueld-dev --local
It deploys to the preview of the current branch, creating it if needed. Add
--slot <name> to give each agent working on one branch a preview of its
own. To preview a pushed branch instead, run
piquelctl preview create piqueld-dev --branch <branch>, or select
New preview on the application’s Previews tab in the dashboard.
Then list the preview’s URLs:
piquelctl preview show piqueld-dev <branch>
For a preview with a slot, add --slot <name> here and when you delete it.
The preview’s site still links to the book on docs.piqueld.dev, since the
site’s links are absolute.
Delete the preview when you are done:
piquelctl preview delete piqueld-dev <branch>
The piquelctl reference
covers the other preview commands, such as pruning the previews of deleted
branches.
Send applications’ telemetry to it
Other applications on the instance can send their own telemetry to the
collector. Uncomment the [[spec.exports]] of
deploy/piqueld-observability.toml, with their names in place of my-app.
Then import the collector in each of them, and point its OpenTelemetry SDK at
it:
[[spec.imports]]
application = "piqueld-observability"
service = "collector"
[[spec.services]]
name = "web"
[spec.services.environment]
OTEL_EXPORTER_OTLP_ENDPOINT = "http://collector.piqueld-observability:4317"
OTEL_EXPORTER_OTLP_PROTOCOL = "grpc"
Build the images locally
From the root of the repository:
docker build --file deploy/docs/Dockerfile --tag piqueld-docs .
docker run --rm --publish 8080:8080 piqueld-docs
The book is then on http://localhost:8080. The marketing site builds the same
way from deploy/site/Dockerfile; to look at it without Docker, see
apps/site. To build it in a
development instance’s engine rather than the host’s,
set DOCKER_HOST to that engine’s socket first.
Troubleshooting
- The deployment fails with
git_build_failed. The build’s output ends with the error. List the builds withpiquelctl builds list --application <id>, where<id>is the IDpiquelctl app show piqueld-devprints, then read one withpiquelctl builds logs <number>. - The first deployment fails with
preparation_timeout. The build took longer thanreconciliation.prepare_timeout_seconds, 300 seconds by default. Deploy again withpiquelctl --timeout 10m app deploy piqueld-dev: the steps that finished stay cached. If it keeps failing, raise the setting. - The deployment fails with
hostname_conflict. Another application holdspiqueld.dev,www.piqueld.dev,docs.piqueld.devorgrafana.piqueld.dev. This instance already serves piqueld.dev: migrate that deployment instead. - A preview’s URLs don’t open. Check that the device you open them from
is on the tailnet, and that
*.preview.piqueld.devresolves to the apps node’s tailnet addresses. The first preview’s certificate,*.preview.piqueld.dev, can take up to 10 minutes to issue. - A push deploys nothing.
piquelctl env show <application> productionsays why on itsSyncline, and GitHub’s Recent Deliveries shows whether the delivery failed. A401means the secret doesn’t match: generate a new one and copy it into GitHub again.