- Add roles/pote: Python/venv deployment role with PostgreSQL, cron jobs - Add playbooks/app/: Proxmox app stack provisioning and configuration - Add roles/app_setup: Generic app deployment role (Node.js/systemd) - Add roles/base_os: Base OS hardening role - Enhance roles/proxmox_vm: Split LXC/KVM tasks, improve error handling - Add IP uniqueness validation: Preflight check for duplicate IPs within projects - Add Proxmox-side IP conflict detection: Check existing LXC net0 configs - Update inventories/production/group_vars/all/main.yml: Add pote project config - Add vault.example.yml: Template for POTE secrets (git key, DB, SMTP) - Update .gitignore: Exclude deploy keys, backup files, and other secrets - Update documentation: README, role docs, execution flow guides Security: - All secrets stored in encrypted vault.yml (never committed in plaintext) - Deploy keys excluded via .gitignore - IP conflict guardrails prevent accidental duplicate IP assignments
6.0 KiB
App stack execution flow (what happens when you run it)
This document describes exactly what Ansible runs and what it changes when you execute the Proxmox app stack playbooks.
Entry points
- Recommended end-to-end run:
playbooks/app/site.yml
- Repo-root wrappers (equivalent):
site.yml(importsplaybooks/site.yml, and you can--tags app)provision_vms.yml(importsplaybooks/app/provision_vms.yml)configure_app.yml(importsplaybooks/app/configure_app.yml)
High-level flow
When you run playbooks/app/site.yml, it imports two playbooks in order:
playbooks/app/provision_vms.yml(Proxmox API changes happen here)playbooks/app/configure_app.yml(SSH into guests and configure OS/app)
Variables that drive everything
All per-project/per-env inputs come from:
inventories/production/group_vars/all/main.yml→app_projects
Each app_projects.<project>.envs.<env> contains:
name(container hostname / inventory host name)vmid(Proxmox CTID)ip(static IP in CIDR form, e.g.10.0.10.101/24)gateway(e.g.10.0.10.1)branch(dev,qa,main)env_vars(key/value map written to/srv/app/.env.<env>)
Proxmox connection variables are also read from inventories/production/group_vars/all/main.yml but are usually vault-backed:
proxmox_host: "{{ vault_proxmox_host }}"proxmox_user: "{{ vault_proxmox_user }}"proxmox_node: "{{ vault_proxmox_node | default('pve') }}"
Phase 1: Provisioning via Proxmox API
File chain
playbooks/app/site.yml imports playbooks/app/provision_vms.yml, which does:
- Validates
app_projectexists (if you passed one) - Loops projects → includes
playbooks/app/provision_one_guest.yml - Loops envs inside the project → includes
playbooks/app/provision_one_env.yml
Preflight IP safety check
In playbooks/app/provision_one_env.yml:
- It runs
pingagainst the target IP. - If the IP responds, the play fails to prevent accidental duplicate-IP provisioning.
- You can override the guard (not recommended) with
-e allow_ip_conflicts=true.
What it creates/updates in Proxmox
In playbooks/app/provision_one_env.yml it calls role roles/proxmox_vm with LXC variables.
roles/proxmox_vm/tasks/main.yml dispatches:
- If
proxmox_guest_type == 'lxc'→ includesroles/proxmox_vm/tasks/lxc.yml
roles/proxmox_vm/tasks/lxc.yml performs:
-
Build CT network config
- Produces a
netifdict like:net0: name=eth0,bridge=vmbr0,firewall=1,ip=<CIDR>,gw=<GW>
- Produces a
-
Create/update the container
- Uses
community.proxmox.proxmoxwith:state: presentupdate: true(so re-runs reconcile config)vmid,hostname,ostemplate, CPU/mem/swap, rootfs sizing,netifpubkeyand optionallypasswordfor initial root access
- Uses
-
Start the container
- Ensures
state: started(iflxc_start_after_create: true)
- Ensures
-
Wait for SSH
wait_for: host=<ip> port=22
Dynamic inventory creation
Still in playbooks/app/provision_one_env.yml, it calls ansible.builtin.add_host so the guests become available to later plays:
- Adds the guest to groups:
app_allapp_<project>_allapp_<project>_<env>
- Sets:
ansible_hostto the IP (without CIDR)ansible_user: root(bootstrap user for first config)app_project,app_envfacts
Phase 2: Configure OS + app on the guests
playbooks/app/configure_app.yml contains two plays:
Play A: Build dynamic inventory (localhost)
This play exists so you can run configure_app.yml even if you didn’t run provisioning in the same Ansible invocation.
- It loops over projects/envs from
app_projects - Adds hosts to:
app_all,app_<project>_all,app_<project>_<env>
- Uses:
ansible_user: "{{ app_bootstrap_user | default('root') }}"
Play B: Configure the hosts (SSH + sudo)
Targets:
- If you pass
-e app_project=projectA→hosts: app_projectA_all - Otherwise →
hosts: app_all
Tasks executed on each guest:
-
Resolve effective project/env variables
project_def = app_projects[app_project]env_def = app_projects[app_project].envs[app_env]
-
Role:
base_os(roles/base_os/tasks/main.yml)- Updates apt cache
- Installs baseline packages (git/curl/nodejs/npm/ufw/etc.)
- Creates
appuser(passwordless sudo) - Adds your SSH public key to
appuser - Enables UFW and allows:
- SSH (22)
- backend port (default
3001, overridable per project) - frontend port (default
3000, overridable per project)
-
Role:
app_setup(roles/app_setup/tasks/main.yml)- Creates:
/srv/app/srv/app/backend/srv/app/frontend
- Writes the env file:
/srv/app/.env.<dev|qa|prod>from templateroles/app_setup/templates/env.j2
- Writes the deploy script:
/usr/local/bin/deploy_app.shfromroles/app_setup/templates/deploy_app.sh.j2- Script does:
git cloneif missinggit checkout/pullcorrect branch- runs backend install + migrations
- runs frontend install + build
- restarts systemd services
- Writes systemd units:
/etc/systemd/system/app-backend.servicefromapp-backend.service.j2/etc/systemd/system/app-frontend.servicefromapp-frontend.service.j2
- Reloads systemd and enables/starts both services
- Creates:
What changes on first run vs re-run
- Provisioning:
- First run: creates CTs in Proxmox, sets static IP config, starts them.
- Re-run: reconciles settings because
update: trueis used.
- Configuration:
- Mostly idempotent (directories/templates/users/firewall/services converge).
Common “before you run” checklist
- Confirm
app_projectshas correct IPs/CTIDs/branches:inventories/production/group_vars/all/main.yml
- Ensure vault has Proxmox + SSH key material:
inventories/production/group_vars/all/vault.yml- Reference template:
inventories/production/group_vars/all/vault.example.yml