Goodberry
Goodberry is designed to provision Arch Linux base images for Hetzner Cloud VMs. It handles provisioning tasks including btrfs filesystem setup, podman configuration, network configuration, user management, SSH key setup, security hardening, system tuning, and image optimization.
Features
- Btrfs Filesystem Configuration: Sets up btrfs with subvolumes and optimized mount options
- Podman Installation: Installs and configures podman with btrfs storage driver
- Network Configuration: Configures systemd-networkd for Hetzner Cloud
- User Management: Creates administrator user with passwordless sudo
- SSH Configuration: Sets up SSH daemon and afterburn for key retrieval
- Security Hardening: Applies security best practices and kernel parameters
- System Tuning: Optimizes kernel parameters and systemd settings
- Firewall Setup: Configures nftables firewall
- Image Optimization: Cleans up and optimizes the image for deployment
- Extra Packages: Supports installing additional packages via configuration
Installation
Building from Source
git clone https://codeberg.org/goodberry/goodberry.git
cd goodberry
go build -o goodberry .
Building with Containerfile
You can build and run goodberry in a container without installing Go locally:
# Build the container image
podman build -t goodberry:latest .
# Or using docker
docker build -t goodberry:latest .
# Run with default configuration
podman run --rm goodberry:latest
# Run with custom config directory
podman run --rm -v /path/to/config:/config goodberry:latest --config /config
# Export configuration files
podman run --rm -v /path/to/output:/output goodberry:latest --export-config /output
The Containerfile uses a multi-stage build with gcr.io/distroless/static-debian13:nonroot for a minimal, secure runtime image (~2MB).
Build Automation
Goodberry includes both justfile (preferred) and Makefile for build automation.
Using justfile (Recommended)
# List all available recipes
just
# Build binary
just build
# Install to GOBIN
just install
# Run tests
just test
# Build container image
just container-build
# Run container with config directory
just container-run /path/to/config
# Export configuration
just export-config /path/to/output
# Clean build artifacts
just clean
Using Makefile
# Build binary
make build
# Install to GOBIN
make install
# Run tests
make test
# Build container image
make container-build
# Run container (uses ./config directory)
make container-run
# Clean build artifacts
make clean
Usage
goodberry [flags]
Flags
--config, -c string: Path to TOML configuration file (optional)
--dry-run: Show what would be done without making changes
--verbose, -v: Enable verbose logging
--skip string: Comma-separated list of modules to skip
--only string: Comma-separated list of modules to run (skips others)
Examples
# Run with default configuration
goodberry
# Run with custom configuration file
goodberry --config /path/to/config.toml
# Run with custom configuration directory (allows file overrides)
goodberry --config /path/to/config/dir
# Export all embedded configuration files
goodberry --export-config ./my-config
# Dry run to see what would be done
goodberry --dry-run --verbose
# Skip specific modules
goodberry --skip firewall,optimize
# Run only specific modules
goodberry --only locale,user,ssh
Configuration
Goodberry uses TOML format for configuration files. If no configuration file is provided, default values are used.
Configuration File Overrides
You can override embedded configuration files by providing a directory with --config. The directory should contain a config.toml file (optional) and can include any of the embedded configuration files to override them.
# Export default configuration files
goodberry --export-config ./my-config
# Customize files in ./my-config/
# Then use your customizations
goodberry --config ./my-config
The exported directory structure matches the embedded file structure:
sysctl/ - Kernel parameter configurations
systemd/ - Systemd unit files and drop-ins
firewall/ - Firewall templates
podman/ - Podman storage configurations
tmpfiles.d/ - Tmpfiles configurations
ssh/ - SSH configuration templates
Example Configuration File
[packages]
extra = [
"vim",
"htop",
"git"
]
[btrfs]
subvolumes = [
{ name = "@", path = "/" },
{ name = "@home", path = "/home" },
{ name = "@var-log", path = "/var/log" },
{ name = "@containers", path = "/var/lib/containers" }
]
mount_options = "compress=zstd,noatime,nodiratime"
[user]
name = "administrator"
groups = ["wheel"]
shell = "/bin/bash"
[ssh]
afterburn_version = "5.3.0"
disable_root_login = true
disable_password_auth = true
[network]
interface = "eth0"
dhcp = true
[locale]
locale = "en_US.UTF-8"
timezone = "UTC"
keyboard = "us"
[firewall]
allow_ssh = true
ssh_port = 22
[security]
lock_root = true
auto_updates = true
[optimize]
clean_package_cache = true
clean_journal = true
journal_retention_hours = 24
Configuration Sections
[packages]
extra: List of additional packages to install via pacman
[btrfs]
subvolumes: List of btrfs subvolumes to create
mount_options: Mount options for btrfs filesystem
[user]
name: Username for the administrator account
groups: List of groups to add the user to
shell: Default shell for the user
[ssh]
afterburn_version: Version of afterburn to install
disable_root_login: Disable root login via SSH
disable_password_auth: Disable password authentication
[network]
interface: Network interface name (e.g., "eth0", "ens3")
dhcp: Enable DHCP
[locale]
locale: System locale (e.g., "en_US.UTF-8")
timezone: System timezone (e.g., "UTC")
keyboard: Keyboard layout (e.g., "us")
[firewall]
allow_ssh: Allow SSH connections
ssh_port: SSH port number
[security]
lock_root: Lock root account
auto_updates: Enable automatic security updates
[optimize]
clean_package_cache: Clean pacman package cache
clean_journal: Clean systemd journal logs
journal_retention_hours: Hours of journal logs to retain
Packer Integration
Goodberry is designed to be used with Packer for creating Hetzner Cloud images. Here's an example Packer configuration:
provisioner "file" {
source = "goodberry"
destination = "/tmp/goodberry"
}
provisioner "shell" {
inline = [
"chmod +x /tmp/goodberry",
"/tmp/goodberry --verbose"
]
}
Or with a configuration file:
provisioner "file" {
source = "goodberry"
destination = "/tmp/goodberry"
}
provisioner "file" {
source = "config.toml"
destination = "/tmp/config.toml"
}
provisioner "shell" {
inline = [
"chmod +x /tmp/goodberry",
"/tmp/goodberry --config /tmp/config.toml --verbose"
]
}
Modules
Goodberry is organized into the following modules:
- Locale: Configures locale, timezone, and keyboard
- Packages: Installs extra packages from configuration
- Btrfs: Sets up btrfs filesystem and subvolumes
- Network: Configures systemd-networkd
- User: Creates administrator user and configures sudo
- SSH: Sets up SSH and afterburn
- Podman: Installs and configures podman
- System Tuning: Applies kernel and systemd optimizations
- Firewall: Configures nftables firewall
- Security: Applies security hardening
- Optimize: Cleans up and optimizes the image
Architecture
goodberry/
├── main.go # Entry point, CLI argument parsing
├── cmd/
│ └── provision.go # Main provisioning orchestration
├── internal/
│ ├── btrfs/ # Btrfs filesystem configuration
│ ├── podman/ # Podman installation and configuration
│ ├── network/ # systemd-networkd configuration
│ ├── user/ # User management
│ ├── ssh/ # SSH configuration
│ ├── security/ # Security hardening
│ ├── system/ # System tuning
│ ├── locale/ # Locale configuration
│ ├── firewall/ # Firewall configuration
│ ├── optimize/ # Image optimization
│ ├── packages/ # Package installation
│ └── systemd/ # Systemd utilities
├── pkg/
│ ├── executor/ # Command execution utilities
│ ├── file/ # File manipulation utilities
│ └── config/ # Configuration structures
Design Principles
- Idempotency: All operations are idempotent and safe to re-run
- Error Handling: Comprehensive error handling with clear error messages
- Logging: Structured logging using slog for debugging and audit trails
- Modularity: Each provisioning task is a separate module
- Testability: Each module can be tested independently
- Configuration: TOML configuration files for flexibility
Dependencies
Go Dependencies
github.com/spf13/cobra - CLI framework
github.com/pelletier/go-toml/v2 - TOML parser
- Standard library (log/slog for logging)
System Dependencies (on target system)
- pacman (package manager)
- systemd
- btrfs-progs
- Various standard Unix utilities
Error Handling
- All functions return errors
- Errors include context using
fmt.Errorf with %w verb
- Log errors using slog before returning
- Exit codes: 0 = success, 1 = general error
Logging
- Structured logging using
log/slog
- Log levels: DEBUG, INFO, WARN, ERROR
- All major operations are logged
- Timing information included for long operations
Testing
Run tests with:
go test ./...
License
This project is licensed under the GNU Affero General Public License v3.0. See LICENSE for details.
Contributing
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
For development workflow and branching strategy, see docs/workflow.md.
For more information, visit the project repository: https://codeberg.org/goodberry/goodberry