Red Panda Approved™ Infrastructure as Code
10 Incus containers named after moons of Uranus, provisioned with Terraform and configured with Ansible. Accessible at ouranos.helu.ca
Ouranos is a comprehensive infrastructure-as-code project that provisions and manages a complete development sandbox environment. All infrastructure and configuration is tracked in Git for reproducible deployments.
DNS Domain: Incus resolves containers via the .incus suffix (e.g., oberon.incus). IPv4 addresses are dynamically assigned — always use DNS names, never hardcode IPs.
Provisions the Uranian host containers with:
.incus domain)Deploys and configures all services:
| Name | Role | Key Services | Nesting |
|---|---|---|---|
| ariel | graph_database | Neo4j 5.26.0 | |
| caliban | agent_automation | Agent S MCP Server, Kernos, MATE Desktop, GPU | |
| miranda | mcp_docker_host | MCPO, Grafana MCP, Gitea MCP, Neo4j MCP, Argos MCP | |
| oberon | container_orchestration | MCP Switchboard, RabbitMQ, Open WebUI, SearXNG, Home Assistant, smtp4dev | |
| portia | database | PostgreSQL 16 | |
| prospero | observability | Prometheus, Loki, Grafana, PgAdmin, AlertManager | |
| puck | application_runtime | JupyterLab, Gitea Runner, Django apps (6×) | |
| rosalind | collaboration | Gitea, LobeChat, Nextcloud, AnythingLLM | |
| sycorax | language_models | Arke LLM Proxy | |
| titania | proxy_sso | HAProxy, Casdoor SSO, certbot |
King of the Fairies orchestrating containers and managing MCP infrastructure.
Intelligent and resourceful — the reliability of relational databases.
arke, anythingllm, gitea, hass, lobechat, mcp_switchboard, nextcloud, openwebui, spelunkerAir spirit — ethereal, interconnected nature mirroring graph relationships.
Shape-shifting trickster embodying Python's versatility.
Master magician observing all events.
Curious bridge between worlds — hosting MCP server containers.
Original magical power wielding language magic.
Autonomous computer agent learning through environmental interaction.
Witty and resourceful moon for PHP, Go, and Node.js runtimes.
Queen of the Fairies managing access control and authentication.
:8404/metricsTitania provides TLS termination and reverse proxy for all services. Base domain: ouranos.helu.ca — HTTPS port 443, HTTP port 80 (redirects to HTTPS). Certificate: Let's Encrypt wildcard via certbot DNS-01 (Namecheap).
| Subdomain | Backend | Service |
|---|---|---|
ouranos.helu.ca root | puck.incus:22281 | Angelia (Django) |
alertmanager.ouranos.helu.ca | prospero.incus:443 SSL | AlertManager |
angelia.ouranos.helu.ca | puck.incus:22281 | Angelia (Django) |
anythingllm.ouranos.helu.ca | rosalind.incus:22084 | AnythingLLM |
arke.ouranos.helu.ca | sycorax.incus:25540 | Arke LLM Proxy |
athena.ouranos.helu.ca | puck.incus:22481 | Athena (Django) |
gitea.ouranos.helu.ca | rosalind.incus:22082 | Gitea |
grafana.ouranos.helu.ca | prospero.incus:443 SSL | Grafana |
hass.ouranos.helu.ca | oberon.incus:8123 | Home Assistant |
id.ouranos.helu.ca | titania.incus:22081 | Casdoor SSO |
icarlos.ouranos.helu.ca | puck.incus:22681 | Icarlos (Django) |
jupyterlab.ouranos.helu.ca | puck.incus:22071 | JupyterLab OAuth2-Proxy |
kairos.ouranos.helu.ca | puck.incus:22581 | Kairos (Django) |
lobechat.ouranos.helu.ca | rosalind.incus:22081 | LobeChat |
loki.ouranos.helu.ca | prospero.incus:443 SSL | Loki |
mcp-switchboard.ouranos.helu.ca | oberon.incus:22785 | MCP Switchboard |
nextcloud.ouranos.helu.ca | rosalind.incus:22083 | Nextcloud |
openwebui.ouranos.helu.ca | oberon.incus:22088 | Open WebUI |
peitho.ouranos.helu.ca | puck.incus:22981 | Peitho (Django) |
pgadmin.ouranos.helu.ca | prospero.incus:443 SSL | PgAdmin 4 |
prometheus.ouranos.helu.ca | prospero.incus:443 SSL | Prometheus |
searxng.ouranos.helu.ca | oberon.incus:22073 | SearXNG OAuth2-Proxy |
smtp4dev.ouranos.helu.ca | oberon.incus:22085 | smtp4dev |
spelunker.ouranos.helu.ca | puck.incus:22881 | Spelunker (Django) |
# Provision containers
cd terraform
terraform init
terraform plan
terraform apply
# Start all containers
cd ../ansible
source ~/env/ouranos/bin/activate
ansible-playbook sandbox_up.yml
# Deploy all services
ansible-playbook site.yml
# Stop all containers
ansible-playbook sandbox_down.yml
# Edit secrets
ansible-vault edit \
inventory/group_vars/all/vault.yml
# View secrets
ansible-vault view \
inventory/group_vars/all/vault.yml
# Encrypt a new file
ansible-vault encrypt new_secrets.yml
*.tf filesterraform planterraform applyapt_update.yml)Terraform provisions Incus S3 buckets for services requiring object storage:
| Service | Host | Purpose |
|---|---|---|
| Casdoor | Titania | User avatars and SSO resource storage |
| LobeChat | Rosalind | File uploads and attachments |
S3 credentials are stored as sensitive Terraform outputs and in Ansible Vault with the vault_*_s3_* prefix.
| Playbook | Host(s) | Purpose |
|---|---|---|
apt_update.yml | All | Update packages and install essentials |
alloy/deploy.yml | All | Grafana Alloy log/metrics collection |
prometheus/node_deploy.yml | All | Node Exporter metrics |
docker/deploy.yml | Oberon, Ariel, Miranda, Puck, Rosalind, Sycorax, Caliban, Titania | Docker engine |
smtp4dev/deploy.yml | Oberon | SMTP test server |
pplg/deploy.yml | Prospero | Full observability stack + internal HAProxy + OAuth2-Proxy |
postgresql/deploy.yml | Portia | PostgreSQL with all databases |
postgresql_ssl/deploy.yml | Titania | Dedicated PostgreSQL for Casdoor |
neo4j/deploy.yml | Ariel | Neo4j graph database |
searxng/deploy.yml | Oberon | SearXNG privacy search |
haproxy/deploy.yml | Titania | HAProxy TLS termination and routing |
casdoor/deploy.yml | Titania | Casdoor SSO |
mcpo/deploy.yml | Miranda | MCPO MCP proxy |
openwebui/deploy.yml | Oberon | Open WebUI LLM interface |
hass/deploy.yml | Oberon | Home Assistant |
gitea/deploy.yml | Rosalind | Gitea self-hosted Git |
nextcloud/deploy.yml | Rosalind | Nextcloud collaboration |
| Playbook | Host | Service |
|---|---|---|
anythingllm/deploy.yml | Rosalind | AnythingLLM document AI |
arke/deploy.yml | Sycorax | Arke LLM proxy |
argos/deploy.yml | Miranda | Argos MCP web search server |
caliban/deploy.yml | Caliban | Agent S MCP Server |
certbot/deploy.yml | Titania | Let's Encrypt certificate renewal |
gitea_mcp/deploy.yml | Miranda | Gitea MCP Server |
gitea_runner/deploy.yml | Puck | Gitea CI/CD runner |
grafana_mcp/deploy.yml | Miranda | Grafana MCP Server |
jupyterlab/deploy.yml | Puck | JupyterLab + OAuth2-Proxy |
kernos/deploy.yml | Caliban | Kernos MCP shell server |
lobechat/deploy.yml | Rosalind | LobeChat AI chat |
neo4j_mcp/deploy.yml | Miranda | Neo4j MCP Server |
rabbitmq/deploy.yml | Oberon | RabbitMQ message queue |
sandbox_up.ymlStart all Uranian host containers
site.ymlFull deployment orchestration
apt_update.ymlUpdate packages on all hosts
sandbox_down.ymlGracefully stop all containers
| Consumer | Provider | Connection |
|---|---|---|
| All LLM apps | Arke (Sycorax) | http://sycorax.incus:25540 |
| Open WebUI, Arke, Gitea, Nextcloud, LobeChat | PostgreSQL (Portia) | portia.incus:5432 |
| Neo4j MCP | Neo4j (Ariel) | ariel.incus:7687 (Bolt) |
| MCP Switchboard | Docker API (Miranda) | tcp://miranda.incus:2375 |
| MCP Switchboard, Kairos, Spelunker | RabbitMQ (Oberon) | oberon.incus:5672 |
| All apps (SMTP) | smtp4dev (Oberon) | oberon.incus:22025 |
| All hosts (logs) | Loki (Prospero) | http://prospero.incus:3100 |
| All hosts (metrics) | Prometheus (Prospero) | http://prospero.incus:9090 |
Every host with alloy in its services list must define alloy_log_level in inventory/host_vars/<host>.incus.yml. The playbook will fail with an undefined variable error if this is missing.
Any Docker Compose service using the syslog logging driver must have a corresponding loki.source.syslog listener in the host's Alloy config template (ansible/alloy/<hostname>/config.alloy.j2). Missing listeners cause Docker containers to fail on start because the syslog driver cannot connect to its configured port.
This project uses local Terraform state (no remote backend). Do not run terraform apply from multiple machines simultaneously.
Docker runs inside Incus containers (nested), requiring security.nesting = true and lxc.apparmor.profile=unconfined AppArmor override on all Docker-enabled hosts.
Prospero (observability) must be fully deployed before other hosts, as Alloy on every host pushes logs and metrics to prospero.incus. Run pplg/deploy.yml before site.yml on a fresh environment.