Fest: solid, immutable system configuration for Arch Linux. All the power of declarative infrastructure, none of the complexity. Written in Go.

package main

import "github.com/emad-elsaid/fest"

func main() {
    // Declare your entire system in Go
    fest.Package("vim", "git", "docker", "firefox")
    fest.SystemService("docker")
    fest.Timedate("America/New_York", true)
    
    fest.Main()  // That's it. Seriously.
}

Why Choose Arch + Go Over NixOS?

Fest

  • Familiar Language: Use Go, not a custom DSL
  • Type Safety: Compiler catches errors before runtime
  • Zero Learning Curve: If you know Go, you're ready
  • Keep Your Distro: Stay on Arch Linux
  • Access to AUR: 80,000+ packages via yay
  • Pacman Speed: No rebuilding everything
  • Standard Tools: systemd, pacman, familiar workflows
  • Rolling Release: Always latest packages

NixOS

  • Custom Nix language to learn
  • Steep learning curve (weeks/months)
  • Must switch entire distro
  • Limited package repository compared to AUR
  • Long rebuild times
  • Non-standard filesystem layout
  • Different package manager
  • Reproducible, but complex

The Philosophy: Simplicity Meets Power

NixOS forces you to learn a new language, adopt a new distro, and rewire your mental model of Linux. Fest gives you immutable infrastructure without the friction.

You already know Go. You already love Arch. Why compromise? Get declarative configuration, reproducible systems, and version-controlled infrastructure—all while keeping the Arch Linux ecosystem you trust.

Feature Fest NixOS Ansible
Language Go (mainstream) Nix (custom DSL) YAML (no type safety)
Type Safety Full compiler validation Some type checking Runtime errors only
Learning Curve Low (standard Go) High (new paradigm) Medium (YAML + modules)
Keep Your Distro Works on Arch Requires NixOS Distro-agnostic
AUR Access Full 80,000+ packages Limited compatibility Via manual tasks
Speed Fast binary installs Slow (rebuilds/downloads) Medium (SSH overhead)
Single Binary Compiled Go binary System-level Python + modules
Declarative Config Full support Full support Idempotent playbooks
Reproducibility Version-controlled Fully reproducible Depends on idempotency

Key Features

📦

Unified Package Management

Manage pacman, AUR, Flatpak, npm, Go packages, and Ruby gems from a single declarative configuration.

🔄

Declarative Everything

Services, timers, sockets, system files, user groups, dotfiles—declare it once, sync everywhere.

🛡️

Type-Safe Configuration

Go's compiler catches typos, missing imports, and logic errors before you run a single command.

Preview Changes

go run . diff shows exactly what would change—no surprises, no accidents.

🧹

Automatic Cleanup

Tracks what it installed. Remove a package from config, it gets removed from your system.

🔗

Dependency Aware

Won't remove packages other wanted packages depend on. Intelligent dependency resolution.

📝

State Capture

go run . save converts your current system state into Go code. Perfect for migrations.

🎯

Lifecycle Hooks

Run custom logic before/after specific resources sync. Full programmatic control.

🤖

LLM Agent Friendly

Go's simplicity and excellent tooling make Fest perfect for AI-assisted system management. LLMs understand Go naturally and leverage its compiler, formatter, and test suite.

Real-World Example

package main

import "github.com/emad-elsaid/fest"

func main() {
    // Base system packages
    fest.Package(
        "base-devel", "vim", "git", "htop",
        "docker", "kubectl", "terraform",
    )
    
    // Development tools
    fest.GoPackage(
        "github.com/golangci/golangci-lint/cmd/golangci-lint@latest",
    )
    
    fest.NpmPackage(
        "typescript", "@vue/cli", "eslint",
    )
    
    // Flatpak apps
    fest.Flatpak(
        "com.slack.Slack",
        "us.zoom.Zoom",
    )
    
    // System configuration
    fest.Timedate("America/New_York", true)
    fest.Locale("en_US.UTF-8 UTF-8")
    
    // Services
    fest.SystemService("docker", "sshd")
    fest.Service("syncthing")
    
    // User groups
    fest.Group("docker", "wheel", "audio")
    
    // System files (from system/ directory)
    fest.SystemFilesDir("system")
    
    // Custom hook: notify after docker install
    fest.After(fest.ResourcePackages, func() {
        // Your custom setup logic here
    })
    
    fest.Main()
}
That's it. Commit this to git. Run go run . apply on any Arch machine. Your entire system state is now reproducible, version-controlled, and portable.

Install Fest in 60 Seconds

# 1. Create your system configuration
mkdir ~/mysystem && cd ~/mysystem
go mod init mysystem
go get github.com/emad-elsaid/fest

# 2. Create main.go (see examples above)

# 3. Preview what would change
go run . diff

# 4. Apply your configuration
go run . apply

# 5. Commit to git and deploy anywhere
git init && git add . && git commit -m "Initial system config"
View on GitHub Read the Docs