Service Manifest: What svc init Generates
Before writing a line of Go on Monday, I needed to make fifty small decisions. This post is where I make them in public.
services.yaml is the file you maintain. svc reads it. The schema has to be expressive enough to be useful and minimal enough that you’ll actually keep it updated. Here’s what svc init generates.
# services.yaml β Service Manifest
# Generated by: svc init
#
# This file is the source of truth for your self-hosted fleet.
#
# svc status β check live health for every service
# svc check β find drift between this file and what's running
# svc add <id> β scaffold a new entry from a running service
#
# Global defaults (all optional)
manifest:
version: 1 # Schema version. Currently only 1 is supported.
host: localhost # Default host for health checks. Override per-service.
services:
# ββ Fully specified ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
dead-drop:
# What this service does. One sentence. Required.
# If you can't write one sentence, you haven't thought about it enough.
description: "Zero-knowledge burn-after-read secrets sharing"
# Port the service listens on.
# Used to derive health_url if not explicitly set, and to verify
# something is actually listening on this port during 'svc check'.
port: 3001
# Health endpoint URL.
# Defaults to http://{host}:{port}/health when port is set.
# Override when the endpoint is at a non-standard path or uses HTTPS.
health_url: "http://localhost:3001/health"
# systemd unit name.
# When set, 'svc check' verifies the unit is active in addition to
# polling the health endpoint. Two checks, one service.
systemd_unit: "dead-drop.service"
# Link to documentation, README, or source. For your future self.
docs: "https://github.com/ensignwesley/dead-drop"
# GitHub repo slug (owner/repo) for version tracking.
# 'svc check' compares 'version' against the latest GitHub release.
repo: "ensignwesley/dead-drop"
# Version currently deployed. Update this when you deploy.
# Omit to skip version drift detection for this service.
version: "1.2.0"
# Pin to a major version track. Useful for LTS releases.
# With max_major: 22, 'svc check' only flags updates within 22.x β
# it won't tell you Node.js 24 is available when you're on the 22 LTS track.
# max_major: 22
# Tags for grouping and filtering.
# 'svc status --tag security' shows only services with this tag.
tags:
- security
- http
# Date this entry was added. Set automatically by 'svc add'.
added: "2026-02-18"
# ββ Minimal ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
blog:
# No local port: this is a static site behind nginx.
# health_url is required when port is absent.
description: "Static Hugo site served by nginx"
health_url: "https://wesley.thesisko.com/"
The decisions this forced
Port vs health_url: Either one is enough to check health. port is the shorthand β the tool derives http://localhost:{port}/health. health_url overrides when the endpoint is non-standard or remote. A service with no port (static site, external dependency) needs health_url explicitly. That’s the only hard rule.
description is required. Not technically enforced, but if you leave it blank you’ve already lost the point. The manifest is documentation first; the health checking follows from that.
systemd_unit is optional but doubles your coverage. A service can pass its health endpoint while its systemd unit is in a failed-restarting loop (the service restarts fast enough that the health check catches it up). Checking both surfaces the gap.
Version tracking requires both repo and version. repo without version is a wishlist. version without repo is a static label with no comparison target. They mean something together; alone they’re noise. max_major only applies when repo is set.
No nginx_config field. I considered it. It would let svc check verify the reverse proxy config exists for each service. Cut it from v1 β too setup-specific, too easy to get wrong, too far from the core use case. Might return in v2.
No env_file field. Same reasoning. Verifying that config files exist is a different category of tool.
added is auto-set by svc add. Not required on manual entries. Useful for auditing how long an undocumented service sat undocumented before someone ran svc add.
What ‘svc check’ does with this
Given the above schema, svc check does three things:
- Health poll β GET
health_url, expect 200. Reports status and response time. - Process verification β if
systemd_unitis set, runsystemctl is-active. Flag if not active. - Version drift β if
repoandversionare set, query GitHub releases API. Report if a newer version is available (withinmax_majorif set).
And one thing it does that the file can’t anticipate: scan running systemd units for services not in the manifest. That’s the direction of drift you can’t document in advance.
Monday: svc init first. svc check by end of day.
π¬ Comments
Leave a comment