Skip to content

Introduction

Get Started

Holden is a self-hosted, GitOps container orchestrator. It runs as a single Docker container, watches your git repos for changes, and deploys your apps automatically.

Docker Compose works, but it’s manual. You SSH into your server, pull changes, run docker-compose up -d, hope nothing breaks. No automatic deploys, no rollbacks, secrets scattered in .env files.

Kubernetes solves these problems but brings its own: steep learning curve, complex YAML, operational overhead. For a homelab or small deployment, it’s overkill.

Self-hosted PaaS tools (web UIs for managing containers) are easier, but they’re stateful. The tool becomes the source of truth, not your git repo. If it breaks, you’re restoring from backups and hoping your config was exported correctly.

GitOps - Your git repo is the source of truth. Push a change, Holden deploys it. No clicking through UIs, no imperative commands. Config changes are reviewable in PRs.

Stateless - Holden has no database. It derives desired state from your git repos and current state from running containers. Your config and secrets live in git, so Holden itself has nothing to back up. Your app data (volumes) still needs backups.

Declarative - You describe what you want in YAML. Holden figures out how to get there. If a container crashes, Holden recreates it. If config changes, Holden updates it.

Batteries included - Need a database? Add needs: postgres: and get a fully configured PostgreSQL with auto-generated credentials. Same for Valkey and Garage.

Orchestrator - The Holden container itself. Runs on your server, watches git repos, and manages app containers. Stateless - restart it anytime, it figures out what’s running.

Apps - A deployment unit. One app = one holden.yml in a git repo. Each app gets its own Docker network, data directory, and optional needs (databases, caches). Apps are registered via the CLI or API.

Services - Containers within an app. Each app defines one or more services (web, worker, scheduler) that share the same network and needs.

Needs - Pre-configured infrastructure. Declare postgres:, valkey:, or garage: and Holden provisions it with auto-generated credentials.

Your app’s repo contains a holden.yml. You register it with Holden, and when you push changes, Holden picks them up.

  • Directorymy-app/ Your app’s repo
    • holden.yml App config
    • holden.vars.yml Variables + encrypted secrets (optional)
holden.yml
services:
web:
image: nginx:latest
domain: mysite.example.com

Push. Holden picks it up via webhook (or within 5 minutes via polling). Nginx is running at https://mysite.example.com with automatic Traefik routing.

  • Docker - Holden runs as a container and manages other containers
  • Traefik - Reverse proxy for routing traffic to your apps
  • A git host - GitHub, Gitea, or any git server
  • age (optional) - For encrypting secrets in your app repos