Release Process for go53
Automated Releases with GitHub Actions
How It Works
When you push a git tag starting with v (e.g., v0.77.0), GitHub Actions automatically:
- GoReleaser builds binaries for all platforms (Linux, macOS, Windows)
- Both applications are bundled:
go53(server) andgo53ctl(CLI tool) - Binaries are published on GitHub Releases with checksums
Release Process
1. Create Version
./bump_version.sh
# Answer with the new version, e.g., 0.77.0
Or manually:
echo "0.77.0" > VERSION
2. Create Git Tag
git tag -a v0.77.0 -m "Release version 0.77.0"
git push origin v0.77.0
Or via bump_version.sh (which does everything automatically).
3. GitHub Actions Runs Automatically
- GitHub Actions takes over and builds everything
- You can monitor progress in Actions
- Binaries are published on the Releases page
Download Binaries
One-liner Installation (Linux/macOS)
The easiest way to install go53 with systemd service support:
curl -fsSL https://raw.githubusercontent.com/TenforwardAB/go53/main/scripts/install.sh | sudo bash
Or with a specific version:
curl -fsSL https://raw.githubusercontent.com/TenforwardAB/go53/main/scripts/install.sh | sudo bash -s v0.77.0
Manual Installation
After release, binaries are available for direct download:
# Linux x86_64
wget https://github.com/TenforwardAB/go53/releases/download/v0.77.0/go53_0.77.0_linux_amd64.tar.gz
# macOS arm64 (Apple Silicon)
wget https://github.com/TenforwardAB/go53/releases/download/v0.77.0/go53_0.77.0_darwin_arm64.tar.gz
# Windows x86_64
wget https://github.com/TenforwardAB/go53/releases/download/v0.77.0/go53_0.77.0_windows_amd64.zip
Extract and install manually:
tar -xzf go53_0.77.0_linux_amd64.tar.gz
sudo install -m 755 go53/go53 /usr/local/bin/
sudo install -m 755 go53/go53ctl /usr/local/bin/
Local Testing Before Release
# Build locally
make build
# Test binaries
./go53 --help
./go53ctl --help
Configuration
.goreleaser.yml- Defines which binaries are built and for which platforms.github/workflows/release.yml- GitHub Actions workflow triggered on tags
Supported Platforms
- Linux: amd64, arm64
- macOS: amd64, arm64 (Intel & Apple Silicon)
- Windows: amd64, arm64
Systemd Service (Linux)
The installation script automatically sets up a systemd service. After installation:
# Start the service
sudo systemctl start go53
# Enable auto-start on boot
sudo systemctl enable go53
# Check service status
sudo systemctl status go53
# View logs
journalctl -u go53 -f
# Stop the service
sudo systemctl stop go53
# Restart the service
sudo systemctl restart go53
Service Configuration
- User:
go53(created automatically) - Config Directory:
/etc/go53 - Data Directory:
/var/lib/go53 - Service File:
/etc/systemd/system/go53.service
Edit the service file to customize:
sudo systemctl edit go53
View Service Logs
# Last 50 lines
journalctl -u go53 -n 50
# Real-time logs
journalctl -u go53 -f
# Today's logs
journalctl -u go53 --since today
Checksums
Each release includes checksums.txt to verify file integrity:
sha256sum -c checksums.txt
Release Highlights
Full change lists are in CHANGELOG.md; this section records what an operator
should know per release.
0.81.0 — Performance foundation
The query path was measured end to end and rebuilt where it mattered; the protocol, API and storage format are unchanged.
| Answer type | 0.80.0 | 0.81.0 |
|---|---|---|
| A (3 addresses) | 6.6 µs · 101 allocs | 1.1 µs · 14 allocs |
| NODATA | 23.6 µs · 338 allocs | 2.0 µs · 10 allocs |
| NXDOMAIN | 38.5 µs · 683 allocs | 3.6 µs · 13 allocs |
Measured on an AMD Ryzen AI 9 365 (GOMAXPROCS=20) with records in the
encoding/json storage shape — the shape every zone has after a restart or
replication — using the single-goroutine benchmarks in
dns/handler_bench_test.go. Times are rounded up; allocations are exact.
Read these as best-case latency for one query, not as a capacity figure. The
benchmark drives the query path from a single goroutine, so a high GOMAXPROCS
adds garbage-collector and scheduler coordination without the concurrency the
real server gets from it: the same build measures roughly twice the per-query
time for allocation-bound answers at GOMAXPROCS=24 as it does pinned to one
physical core, while compute-bound denial answers barely move. Allocations per
operation are deterministic and reproduce on any machine; the microsecond
figures do not, so compare them only against runs under the same conditions.
Further benchmarking on server-grade CPUs is planned for a later release.
- Name validation no longer compiles a regular expression on every lookup.
- Owner index per zone: existence, wildcard, referral and DNSSEC-denial checks are one map lookup per label instead of a scan of every record type.
- One record decoder for the query path, zone transfers and DNSSEC signing, so the three always agree on a zone’s contents. Records written in a shape that only some readers understood (for example AAAA records produced by ALIAS flattening) are now served, signed and transferred consistently.
- Deletes are safer: deleting one value from an RRset refuses instead of emptying the RRset when the stored value cannot be decoded.
- Direct NSEC3 queries (
QTYPE=NSEC3for a hash owner) match again; a regression since 0.80.0 that did not affect authenticated denial.
Behaviour to be aware of:
- A stored record without a
ttlis served with 3600 seconds on every path (some paths used to serve 0). - A DS/CDS stored with
ttl: 0is served with 0 rather than 3600. - No data migration; downgrading to 0.80.x is possible.
Upgrade path verified from 0.79.0, 0.79.2 and 0.80.0 directly to 0.81.0. See Query Path & Record Storage for the cost model and the storage roadmap (0.82 canonical Merkle hash, 0.84 storage format v2).
0.80.0 — Canonical names
- Zone and owner names are stored and matched in lower case. On the first start, keys written in mixed case are migrated and legacy keys removed; do not downgrade below 0.80 afterwards.
- Fallback case-insensitive scans on the query path are gone.