Skip to content
This repository was archived by the owner on Sep 5, 2026. It is now read-only.

Repository files navigation

Homeserver Infrastructure as Code

Homeserver infrastructure built with Terraform and Ansible on Proxmox VE.

Architecture

┌───────────────────────────────────────────────────────┐
│                      PROXMOX VE                       │
│        32GB RAM │ 1TB SSD │ Ryzen 7 7730U             │
├───────────────────────────────────────────────────────┤
│  Infrastructure VMs (Rocky Linux 10)                  │
│  ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────────┐  │
│  │   IdM   │ │  Vault  │ │ Monitor │ │ PostgreSQL  │  │
│  │ 4G/40G  │ │ 4G/40G  │ │ 4G/200G │ │   4G/100G   │  │
│  └─────────┘ └─────────┘ └─────────┘ └─────────────┘  │
├───────────────────────────────────────────────────────┤
│  Application VMs (Rocky Linux 10)                     │
│  ┌─────────┐ ┌─────────┐ ┌─────────┐                  │
│  │  App 1  │ │  App 2  │ │   ...   │                  │
│  └─────────┘ └─────────┘ └─────────┘                  │
└───────────────────────────────────────────────────────┘

Directory Structure

This repository is split into two parts:

homeserver-iac/
├── infrastructure/          # Core infrastructure services
│   ├── terraform/           # VMs: IdM, Vault, Monitoring, PostgreSQL
│   └── ansible/             # Configuration for infrastructure
│
└── applications/            # Application VMs (separate management)
    ├── terraform/           # VMs: Custom applications
    └── ansible/             # Configuration for applications

Technology Stack

Component Tool Purpose
Hypervisor Proxmox VE VM management
OS Rocky Linux 10 Base
Provisioning Terraform Create VMs
Configuration Ansible Configure VMs
Identity FreeIPA LDAP, Kerberos, CA
Secrets HashiCorp Vault Secret management
Management Cockpit Web-based server management (per-host)
Monitoring Loki + Prometheus + Grafana Logs and metrics
Database PostgreSQL 17 Application database

Network Layout

VM Hostname Ports
IdM idm.home.local 80, 443, 389, 636, 88, 464, 9090
Vault vault.home.local 8200, 9090
Monitoring mon.home.local 3000, 3100, 9091, 9090
PostgreSQL db.home.local 5432, 9090

Note: Port 9090 is Cockpit (web-based management) on all hosts. Prometheus uses 9091.

Prerequisites

Proxmox VE

  • Proxmox VE 8.x installed on the server

Local Machine

  • Terraform >= 1.5.0
  • Ansible >= 2.15
  • Python 3.9+

Rocky Linux 10 Template

1. Download Rocky Linux ISO

Download the minimal ISO from rockylinux.org:

  • Rocky-10-latest-x86_64-minimal.iso

Upload to Proxmox storage (Datacenter → Storage → ISO Images → Upload).

2. Generate SSH Key

# Generate SSH key if not already available
# No need to setup a password, as later, this account will be disabled on active hosts.
ssh-keygen -t ed25519 -f ~/.ssh/homeserver_setup -C "homeserver-setup"

# Display public key (needed during installation)
cat ~/.ssh/homeserver_setup.pub

3. Create VM and Install Rocky Linux

# On Proxmox console - create VM
qm create 9000 --name rocky10-template --memory 2048 --cores 2 \
  --cpu host --machine q35 --agent enabled=1 --net0 virtio,bridge=vmbr0
qm set 9000 --scsihw virtio-scsi-pci
qm set 9000 --scsi0 local-lvm:32
qm set 9000 --ide2 local:iso/Rocky-10-latest-x86_64-minimal.iso,media=cdrom
qm set 9000 --boot order=ide2
qm start 9000

Open the VM console and complete installation:

  • Set root password (or disable root)
  • Create user setup with administrator privileges
  • Set timezone
  • Use automatic partitioning

After installation completes, stop the VM and remove the ISO:

# On Proxmox console
qm stop 9000
qm set 9000 --ide2 none
qm set 9000 --boot order=scsi0
qm start 9000

4. Post-Installation Setup

After installation completes and VM reboots, access via console (or SSH):

# Login as setup user, then:

# Use official Rocky Linux mirror (avoids out-of-sync regional mirrors)
sudo sed -i 's|^mirrorlist=|#mirrorlist=|g' /etc/yum.repos.d/rocky*.repo
sudo sed -i 's|^#baseurl=http://dl.rockylinux.org|baseurl=https://download.rockylinux.org|g' /etc/yum.repos.d/rocky*.repo
sudo dnf clean all
sudo dnf makecache

# Update all packages
sudo dnf update -y

# Install required packages
sudo dnf install -y qemu-guest-agent cloud-init vim nc

# Enable guest agent
sudo systemctl enable --now qemu-guest-agent

# Add SSH key
mkdir -p ~/.ssh
chmod 700 ~/.ssh
echo "YOUR_PUBLIC_KEY_HERE" >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys

# Configure cloud-init for Proxmox
echo "datasource_list: [NoCloud, None]" | sudo tee /etc/cloud/cloud.cfg.d/99-proxmox.cfg

# Reboot
sudo reboot

# Check
sudo systemctl status qemu-guest-agent
cat /etc/cloud/cloud.cfg.d/99-proxmox.cfg
cat ~/.ssh/authorized_keys

# Clean up for template
sudo cloud-init clean
sudo truncate -s 0 /etc/machine-id
sudo shutdown -h now

5. Convert to Template

# On Proxmox console
qm stop 9000
qm set 9000 --delete ide2
qm set 9000 --ide2 local-lvm:cloudinit
qm set 9000 --ciuser setup
qm set 9000 --boot order=scsi0
qm template 9000

Create Proxmox API Token

# On Proxmox console
pveum user add terraform@pve
pveum aclmod / -user terraform@pve -role Administrator
pveum user token add terraform@pve terraform --privsep=0

Infrastructure Setup

The infrastructure provides core services: IdM, Vault, Monitoring, PostgreSQL.

1. Configure Terraform

cd infrastructure/terraform
cp terraform.tfvars.example terraform.tfvars
# Edit terraform.tfvars with actual values

2. Provision VMs

terraform init
terraform plan
terraform apply

Important: After provisioning, reboot all VMs to ensure clean state.

3. Configure Ansible

cd ../ansible

# Copy and edit inventory (update IPs to match terraform.tfvars)
cp inventory/hosts.yml.example inventory/hosts.yml

# Copy and edit network variables
cp inventory/group_vars/all/network.yml.example inventory/group_vars/all/network.yml

# Copy and edit software versions
cp inventory/group_vars/all/versions.yml.example inventory/group_vars/all/versions.yml

Note: Grafana and Alloy install the latest version from their repos automatically.

# Create vault for secrets
ansible-vault create inventory/group_vars/all/vault.yml

Add the following variables to vault.yml:

# IdM
vault_idm_admin_password: "YourSecurePassword123!"
vault_idm_dm_password: "YourSecurePassword123!"
vault_idm_enroller_password: "YourSecurePassword123!"  # For client enrollment

# Grafana
vault_grafana_admin_password: "YourSecurePassword123!"

# Monitoring Authentication
vault_monitoring_user: "monitor"
vault_monitoring_password: "YourSecurePassword123!"
vault_loki_tenant_id: "homeserver"

Note: The IdM setup creates a dedicated enroller account with the Host Enrollment role. This account is used for client enrollment instead of the admin password.

4. Run Ansible

# Install required collections
ansible-galaxy collection install -r requirements.yml

# Test connectivity
ansible all -m ping --ask-vault-pass

# Run playbooks in order
ansible-playbook playbooks/00-base.yml --ask-vault-pass
ansible-playbook playbooks/01-idm.yml --ask-vault-pass
ansible-playbook playbooks/02-enrol.yml --ask-vault-pass
ansible-playbook playbooks/03-vault.yml --ask-vault-pass
ansible-playbook playbooks/04-monitoring.yml --ask-vault-pass
ansible-playbook playbooks/05-postgresql.yml --ask-vault-pass

5. Generate and Upload SSH Key to IdM

Before disabling the setup user, generate a new SSH key for IdM access (make sure to use a password for the SSH key):

# On local machine
ssh-keygen -t ed25519 -f ~/.ssh/homeserver_admin -C "homeserver-admin"
cat ~/.ssh/homeserver_admin.pub
# On IdM server - upload the public key to admin account
ssh setup@idm.home.local
kinit admin
ipa user-mod admin --sshpubkey="ssh-ed25519 AAAA... homeserver-admin"
# Test access
ssh admin@idm.home.local -i ~/.ssh/homeserver_admin

6. Finalise and Disable Setup User

ansible-playbook playbooks/06-finalise.yml --ask-vault-pass

Warning: After this step, the setup user is disabled. Use IdM credentials for all future access.

Playbook Execution Order

Order Playbook Description Dependencies
1 00-base.yml Base hardening + Cockpit None
2 01-idm.yml IdM server Base
3 02-enrol.yml IdM enrollment IdM server
4 03-vault.yml Vault IdM enrollment
5 04-monitoring.yml Monitoring IdM enrollment
6 05-postgresql.yml PostgreSQL IdM enrollment
7 06-finalise.yml Disable setup user All above

Application VMs

After infrastructure is running, you can add application VMs.

1. Configure Terraform

cd applications/terraform
cp terraform.tfvars.example terraform.tfvars
# Edit terraform.tfvars - add your VMs to the 'vms' map

Example VM configuration:

vms = {
  myapp = {
    vmid        = 131
    ip_address  = "10.10.10.31/24"
    memory      = 2048
    disk_size   = 40
    cpu_cores   = 2
    description = "My Application Server"
    tags        = ["web", "production"]
  }
}

2. Provision VMs

terraform init
terraform plan
terraform apply

3. Configure Ansible

cd ../ansible

# Generate inventory from Terraform output (or create manually)
cp inventory/hosts.yml.example inventory/hosts.yml

# Copy network config (must match infrastructure values)
cp inventory/group_vars/all/network.yml.example inventory/group_vars/all/network.yml

# Create vault for secrets
ansible-vault create inventory/group_vars/all/vault.yml

Add to vault.yml:

# Use the enroller password (created automatically by infrastructure IdM setup)
vault_idm_enroller_password: "your-enroller-password"

4. Run Ansible

# Run playbooks
ansible-playbook playbooks/00-base.yml --ask-vault-pass
ansible-playbook playbooks/01-enrol.yml --ask-vault-pass
ansible-playbook playbooks/02-alloy.yml --ask-vault-pass  # Optional: monitoring

5. Finalise and Disable Setup User

ansible-playbook playbooks/03-finalise.yml --ask-vault-pass

Warning: After this step, the setup user is disabled. Use IdM credentials for all future access.


Post-Installation Tasks

Delete Proxmox API Token (If Required)

# On Proxmox console
pveum user delete terraform@pve

Create a user in the IDM for server access.

This can be done via the Web UI. It's a security risk to use the main IDM admin on other servers.

Access Web UIs

Service URL Default User
IdM https://idm.home.local admin
Vault https://vault.home.local:8200 (token auth)
Grafana http://mon.home.local:3000 admin
Prometheus http://mon.home.local:9091 (vault_monitoring_user)
Cockpit https://<any-host>:9090 (IdM users)

Development

Pre-commit Hooks

# Install pre-commit
pip install pre-commit

# Install hooks
pre-commit install

# Run manually
pre-commit run --all-files

Linting

# Terraform
terraform fmt -recursive
terraform validate

# Ansible
ansible-lint playbooks/ roles/
yamllint ansible/

About

My home server automation

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Used by

Contributors

Languages