Configuration Reference
Complete reference for colony.toml, environment variables, and runtime configuration options.
Colony reads colony.toml to figure out what to run, where to run it, and how to route traffic to it.
colony.toml Structure
A complete colony.toml example:
[colony]
name = "my-app"
template = "web-app"
description = "My awesome application"
[colony.environment]
node_version = "22"
package_manager = "bun"
rust_version = "1.83"
[colony.services.web]
command = "npm run dev"
port = 3000
working_dir = "frontend"
env = { NODE_ENV = "development" }
[colony.services.api]
command = "cargo run --release"
port = 8080
working_dir = "backend"
env = { RUST_LOG = "info" }
[colony.database]
type = "sqlite"
path = "data/app.db"
[colony.dns]
enabled = true
Configuration Sections
[colony]
Basic metadata.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
name | string | Yes | — | Colony identifier (alphanumeric + hyphens) |
template | string | No | "generic" | Template preset (web-app, api, fullstack) |
description | string | No | "" | What this colony does |
Example:
[colony]
name = "payment-service"
template = "api"
description = "Payment processing microservice"
[colony.environment]
Tell Mycelium which runtimes to provision.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
node_version | string | No | Latest LTS | Node.js version (e.g., "22", "20.10.0") |
package_manager | string | No | "npm" | Package manager (npm, bun, pnpm, yarn) |
rust_version | string | No | Latest stable | Rust toolchain version |
python_version | string | No | System default | Python version |
go_version | string | No | Latest | Go version |
Phase 1 does basic environment provisioning. Phase 2+ will integrate Nix for full reproducibility.
Example:
[colony.environment]
node_version = "22"
package_manager = "bun"
rust_version = "1.83"
python_version = "3.12"
[colony.services.*]
Services Colony spawns. Each gets its own process and Caddy route.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
command | string | Yes | — | Shell command to start the service |
port | integer | Yes | — | Port the service listens on |
working_dir | string | No | Repository root | Working directory for the command |
env | table | No | {} | Environment variables (key-value pairs) |
health_check | string | No | "" | HTTP endpoint to check health |
Example:
[colony.services.web]
command = "npm run dev"
port = 3000
working_dir = "apps/frontend"
env = { NODE_ENV = "development", API_URL = "http://api.colony.local" }
health_check = "/health"
[colony.services.api]
command = "cargo run --release"
port = 8080
working_dir = "apps/backend"
env = { RUST_LOG = "debug", DATABASE_URL = "sqlite://data/app.db" }
Service URLs:
With DNS enabled:
http://{colony-name}-{port}.colony.local
So name = "my-app" and port = 3000 gives you:
http://my-app-3000.colony.local
[colony.database]
Database config. Each colony gets its own isolated instance.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
type | string | No | "sqlite" | Database type (sqlite, postgres, mysql) |
path | string | No | "data/colony.db" | SQLite file path (relative to workspace) |
host | string | No | "localhost" | Host for networked databases |
port | integer | No | Varies | Port for networked databases |
Example (SQLite):
[colony.database]
type = "sqlite"
path = "data/app.db"
Example (Postgres):
[colony.database]
type = "postgres"
host = "localhost"
port = 5432
database = "my_colony_db"
user = "colony"
password = "secret"
Phase 1 focuses on SQLite. Postgres/MySQL require Soil (shared infrastructure) which is planned for Phase 2.
[colony.dns]
DNS resolution configuration for *.colony.local domains.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
enabled | boolean | No | true | Enable DNS resolution via dnsmasq |
Example:
[colony.dns]
enabled = true
Environment Variables
Colony reads environment variables for global configuration:
| Variable | Default | Description |
|---|---|---|
COLONY_HOME | ~/.colony | Base directory for workspaces and data |
COLONY_LOG_LEVEL | info | Log level (debug, info, warn, error) |
MYCELIUM_PORT | 8000 | HTTP API port for Mycelium |
CADDY_ADMIN_PORT | 2019 | Caddy JSON API port for route registration |
Set these before starting Mycelium:
export COLONY_LOG_LEVEL=debug
export MYCELIUM_PORT=9000
gleam run
Caddy Configuration
Colony uses Caddy’s JSON API for dynamic routes. No config files. No reloads. Routes added/removed at runtime.
Dynamic Route Registration
When a colony spawns:
- Mycelium reads
colony.tomlservices - For each service, POSTs a route to
http://localhost:2019/config/apps/http/servers/colony/routes
Route pattern:
{
"match": [
{
"host": ["{colony-name}-{port}.colony.local"]
}
],
"handle": [
{
"handler": "reverse_proxy",
"upstreams": [
{
"dial": "localhost:{port}"
}
]
}
]
}
Manual Caddy Inspection
To view current Caddy configuration:
curl http://localhost:2019/config/ | jq
This shows all registered routes. Useful for debugging DNS/routing issues.
Port Allocation Strategy
Ports are explicit. You declare them in colony.toml.
- Services declare their ports
- Mycelium checks for conflicts
- Caddy routes by hostname (not port)
Why explicit ports?
- Predictable —
web = 3000always means port 3000 - Debuggable — No guessing which port
- Safe — Mycelium rejects colonies with overlapping ports
Use high ports (3000-9999) to avoid system reserved ports (0-1023) and common services (e.g., Postgres on 5432).
DNS Setup (dnsmasq)
Run scripts/setup-dns.sh to configure dnsmasq for *.colony.local.
What it does:
- Installs dnsmasq (if needed)
- Listens on
127.0.0.1:5354 - Adds wildcard rule:
address=/colony.local/127.0.0.1 - On macOS, creates
/etc/resolver/localto route.localqueries to dnsmasq
Verify:
dig @127.0.0.1 -p 5354 test.colony.local
# Should return 127.0.0.1
Why not /etc/hosts?
/etc/hosts needs one entry per hostname. With dynamic colonies, that’s unmanageable. dnsmasq wildcards fix this.
Configuration Validation
Mycelium validates colony.toml on spawn. Common errors:
| Error | Cause | Fix |
|---|---|---|
name must be alphanumeric | Invalid characters in name | Use only a-z, 0-9, - |
port conflict | Another colony uses this port | Change port in colony.toml |
service command empty | Missing command field | Add required command field |
unknown template | Invalid template value | Use valid template or omit |
Template Presets
Templates give you sensible defaults for common stacks:
| Template | Default Environment | Example Services |
|---|---|---|
web-app | Node.js 22, Bun | web on port 3000 |
api | Rust 1.83 | api on port 8080 |
fullstack | Node.js + Rust | web (3000), api (8080) |
generic | None | User-defined |
Override anything:
[colony]
template = "web-app" # Sets node_version=22
[colony.environment]
node_version = "20" # Override to Node 20
Next Steps
- Nix Setup — Understanding the development environment
- Networking Deep Dive — How DNS, Caddy, and namespaces work together
- Security Model — Understanding isolation guarantees