Files
ntbops-ansible/README.md
T

8.1 KiB

nbops-ansible

nbops

Infrastructure as Code for notBoris' Homelab

nbops is the central operations repository for my hosted infrastructure, containing everything required to provision, configure, and maintain my virtual machines and services. The goal is to treat my infrastructure as code for reproducibility and automation from source control, while having minimal manual intervention.

About

nbops-ansible contains the configuration management layer of the nbops platform.

Every virtual machine begins life as a standardised Debian 13 GenericCloud template, provisioned by Proxmox and bootstrapped with Cloud-Init. Once the operating system has completed its initial configuration, the host runs ansible-pull against this repository to configure itself according to its assigned role.

A freshly created virtual machine should require little more than a hostname, network configuration and SSH key before automatically configuring itself into a production-ready host.

Design Philosophy

This repository follows a layered approach where each component has a clearly defined responsibility.

flowchart TD
    A[GitHub: nbops-ansible] --> B[Proxmox VE]
    B --> C[Debian 13 GenericCloud Template]
    C --> D[Clone VM]
    D --> E[Cloud-Init Bootstrap]
    E --> F[ansible-pull]
    F --> G[Apply Base Roles]
    G --> H[Apply Service Roles]
    H --> I[Docker Compose]
    I --> J[Running Services]

Layer Responsibilities

flowchart LR
    P[Proxmox VE<br/>VM lifecycle<br/>storage<br/>networking]
    D[Debian 13<br/>GenericCloud<br/>minimal OS]
    C[Cloud-Init<br/>identity<br/>network<br/>bootstrap]
    A[Ansible<br/>desired state<br/>roles<br/>configuration]
    DC[Docker Compose<br/>service runtime]
    S[Applications<br/>actual workloads]

    P --> D --> C --> A --> DC --> S

Repository Structure

. ├── inventories/ │ ├── production/ │ └── development/ │ ├── playbooks/ │ ├── roles/ │ ├── base/ │ ├── docker/ │ ├── ssh/ │ ├── ufw/ │ ├── mtls/ │ ├── monitoring/ │ ├── traefik/ │ ├── authentik/ │ ├── immich/ │ ├── gitea/ │ ├── arr/ │ ├── ocis/ │ └── pterodactyl/ │ ├── group_vars/ ├── host_vars/ ├── templates/ ├── files/ ├── scripts/ └── docs/

As the infrastructure grows, additional functionality should be implemented as new roles rather than modifying existing ones wherever practical.

Configuration Workflow

Cloud-Init is intentionally lightweight.

Its responsibility is limited to preparing the operating system by configuring:

  • Hostname
  • Networking
  • DNS
  • Users
  • SSH
  • Docker
  • Firewall
  • Internal CA trust
  • Base packages

Once complete, Cloud-Init hands over to Ansible.

Ansible is responsible for configuring and maintaining the desired state of every machine for the remainder of its lifecycle.

Roles

The repository is built around reusable Ansible roles; every server receives a common baseline before additional service-specific roles are applied.

Core Roles

  • base
  • docker
  • ssh
  • ufw
  • mtls
  • monitoring
  • updates

Service Roles

  • traefik
  • authentik
  • gitea
  • Pelican
  • etc..

This approach allows new services to inherit the same security, logging and operating system configuration while remaining independent from one another.

Operating System Standard

Unless otherwise required, every virtual machine deployed by nbops follows the same baseline.

  • Debian 13 (Trixie)
  • GenericCloud image
  • UEFI (OVMF)
  • Q35 machine type
  • VirtIO networking
  • VirtIO SCSI storage
  • QEMU Guest Agent
  • Docker Engine
  • Docker Compose Plugin
  • Chrony
  • UFW
  • SSH key authentication only

Standardising the operating system significantly reduces maintenance overhead and configuration drift across the environment.

Infrastructure Principles

The following principles guide every decision made within this repository.

  • Everything should be reproducible.
  • Everything should be version controlled.
  • Manual configuration should be avoided.
  • Infrastructure should be disposable rather than unique.
  • Security should be built into the baseline.
  • Applications should remain isolated from the operating system wherever possible.
  • Configuration should be modular and reusable.

Future Goals

Planned work includes:

  • Automated Debian template creation
  • Cloud-Init template generation
  • Automated certificate management
  • Secret management
  • Monitoring and alerting
  • Backup automation
  • Configuration validation
  • Disaster recovery testing
  • Multi-node infrastructure support

Contributing

This repository is primarily maintained for my own homelab, but the techniques and playbooks may be useful to others building similar self-hosted environments.

Suggestions, improvements and ideas are always welcome.

License

Unless otherwise stated, all code within this repository is released under the MIT License.

nbops-ansible

Ansible repository for a reproducible Proxmox-based Infrastructure as Code platform using Debian 13 GenericCloud, Cloud-Init, Docker Engine, Docker Compose, and ansible-pull.

Model

Proxmox
  -> Debian 13 GenericCloud template
  -> clone
  -> Cloud-Init
  -> ansible-pull
  -> base roles
  -> service roles
  -> Docker Compose

Cloud-Init handles Day 0 bootstrap only. Ansible owns Day 1 onward state.

Layout

  • inventories/: production and development inventories
  • playbooks/: site and pull entrypoints
  • roles/: reusable core and application roles
  • group_vars/: shared defaults
  • host_vars/: per-VM overrides
  • templates/: Cloud-Init and shared templates
  • scripts/: local helper scripts
  • docs/: operational notes

Roles

Core roles:

  • base
  • ssh
  • ufw
  • updates
  • docker
  • mtls
  • monitoring

Application roles:

  • traefik
  • authentik
  • immich
  • gitea
  • ocis
  • pterodactyl
  • arr

Enable roles per host with nbops_roles.

nbops_roles:
  - base
  - ssh
  - ufw
  - updates
  - docker
  - mtls
  - monitoring
  - traefik

VM Topology

Every VM is expected to run Docker. Put docker in nbops_roles for each VM.

The edge VM normally runs traefik and authentik, with Traefik publishing ports 80 and 443. Downstream VMs can run their own internal Traefik and set nbops_service_routing_mode: traefik so applications register with Traefik instead of exposing individual host ports.

See host_vars/edge.yml.example, host_vars/downstream.yml.example, and docs/topology.md. The full Docker service list is in docs/service-catalog.md.

Compose Updates

Compose-backed roles deploy from role-owned Jinja templates, not directly from upstream examples. Upstream README and Compose examples are tracked as copied snapshots under each role's upstream/ directory and indexed in docs/upstream-sources.yml.

Use compose_paths for Compose files inside an upstream repository and compose_urls for direct raw HTTP Compose YAML fallbacks. Use role-level local_compose_file for Compose reference material already stored in this repository. README fenced Compose blocks are parsed when no explicit Compose file is available.

Use docs/compose-update-policy.md when updating services so upstream drift, NBOPS overrides, image pins, and generated Compose files stay reviewable in Git.

To test upstream references for one role:

scripts/vendor-upstream-snapshots.py jellyfin

To refresh copied upstream references:

scripts/vendor-upstream-snapshots.py --apply jellyfin

Bootstrap

Render templates/cloud-init-user-data.yml.j2 with host variables and pass it to Proxmox as user-data. The template installs baseline packages, Docker from Docker's official Debian repository, configures UFW and SSH, creates /srv and PKI directories, and executes ansible-pull.

Secrets

Secrets are intentionally excluded. Do not commit passwords, API keys, certificates, private keys, or generated .env files. Use Ansible Vault, SOPS, Vault, or Smallstep-backed issuance when introducing real secret material.

Validation

ansible-galaxy collection install -r requirements.yml
scripts/check.sh