Step 01 of 06
Learn the concept
Configuration is where a service changes without a rebuild. It is also where bad input becomes a production outage before main has finished. A monitor should reject htps:// at startup, not discover it on the first incident call.
The ideas this is made of
Flags are excellent for one run
Command-line flags are visible in shell history, easy to document and hard to mistake for ambient state. They are ideal for -config=targets.yaml or -interval=30s during development. They are less pleasant for long target lists because quoting arrays on a command line is a small punishment invented by terminals.
Environment variables belong to the process
Environment variables travel well through systemd, containers and CI. They are strings, so every value needs parsing and error messages. They are also easy to inherit accidentally. Use them for coarse settings such as BEACON_INTERVAL=1m, not for secrets in this course and not for a hundred URLs.
Config files need schemas even when YAML does not
YAML accepts many shapes. Your service should not. A targets list with name and url fields is a contract, and startup validation enforces it before scheduling begins. The error should say targets[2].url: unsupported scheme ftp, not invalid config. One helps; one sulks.
Resolved config is the only config
After loading defaults, file, environment and flags, collapse them into one Config value. Every later package receives that value, not access to flags or os.Getenv. That keeps precedence in one place and makes tests boring in the best possible way.
package main
import (
"fmt"
"net/url"
)
type Target struct{ Name, URL string }
func main() {
t := Target{Name: "docs", URL: "https://example.com"}
u, err := url.ParseRequestURI(t.URL)
if err != nil || u.Scheme != "https" {
fmt.Println("bad target")
return
}
fmt.Println(t.Name, u.Host)
}The important move is parsing before use. Beacon's full loader will read YAML and merge sources, but every target still passes through the same URL validation gate before the scheduler sees it.
Where each setting belongs
| Source | Best for | Bad fit |
|---|---|---|
Flag | Local override | Long lists |
Environment | Supervisor setting | Structured data |
YAML file | Targets and names | One-off debug |
Default | Safe baseline | Site-specific values |
What these are called on the job
Precedence — The rule deciding which source wins when two set the same field.
Fail fast — Stop during startup with a useful error rather than running with invalid state.
Schema — The shape and meaning of accepted configuration, even when the file format is loose.
