← Docs · Guides

Colony Templates

Configure colony environments with colony.toml — define services, ports, environment setup, and provisioning.

Colony templates define the structure, environment, and services for a colony. Everything lives in colony.toml files — Node.js versions, service ports, build commands, the whole setup.

colony.toml Format

A colony.toml file has four main sections:

  1. [colony] — Metadata and template selection
  2. [colony.environment] — Provisioning configuration (runtime versions, package managers)
  3. [colony.ports] — Named port mappings (deprecated, use [services] instead)
  4. [services] — Service definitions with commands and ports
  5. [terminal] — Terminal session configuration

Minimal Example

[colony]
name = "hello-colony"

[services.web]
command = "python3 -m http.server 4001"
port = 4001

One web service on port 4001. That’s it.

Full Example (seed/hello-colony)

[services.web]
command = "python3 -m http.server 4001"
port = 4001

[terminal]
command = "bash"

You get:

  • web service on port 4001 (serves static files via Python’s built-in HTTP server)
  • terminal running bash (accessible via Bloom’s terminal tab)
Service-Driven Tabs

Each [services.*] entry creates a dedicated tab in Bloom’s preview panel. Services named web or api get specialized chrome (iframe sandbox for web, JSON viewer for API).

Section Reference

[colony]

Metadata and template selection.

FieldTypeRequiredDescription
namestringYesHuman-readable colony name (alphanumeric + hyphens)
templatestringNoTemplate identifier (web-app, api-service, full-stack)
descriptionstringNoBrief summary of colony purpose

Example:

[colony]
name = "my-app"
template = "web-app"
description = "Frontend application with API backend"

Templates are pre-configured setups stored in ~/.colony/templates/. When you specify a template, Colony copies its colony.toml and merges your overrides.

[colony.environment]

Environment provisioning. Mycelium uses this to install runtimes, package managers, and tooling.

FieldTypeRequiredDescription
node_versionstringNoNode.js version (e.g., “22”, “20.11.0”)
package_managerstringNoPackage manager (npm, yarn, pnpm, bun)
python_versionstringNoPython version (e.g., “3.12”, “3.11.5”)
ruby_versionstringNoRuby version (e.g., “3.3”, “3.2.1”)
rust_versionstringNoRust toolchain (e.g., “stable”, “1.75.0”)

Example:

[colony.environment]
node_version = "22"
package_manager = "bun"
python_version = "3.12"

Provisioning Process:

  1. Clone repository into ~/.colony/workspaces/{name}
  2. Install requested runtime versions (via mise, asdf, or direct download)
  3. Install dependencies (npm install, pip install -r requirements.txt, etc.)
  4. Create isolated network namespace
  5. Initialize Jujutsu workspace
  6. Spawn SQLite database

If provisioning fails, we roll back and report the error via logs.

Provisioning Failures

If a colony gets stuck in provisioning, check logs via Bloom’s log viewer or Mycelium’s log stream endpoint. Common issues: missing runtime versions, network timeouts, invalid package.json.

[colony.ports] (Deprecated)

Legacy port mapping. Use [services] instead.

FieldTypeRequiredDescription
webintegerNoWeb service port (e.g., 3000)
apiintegerNoAPI service port (e.g., 8080)

Migrating to [services]:

# Old (deprecated)
[colony.ports]
web = 3000
api = 8080

# New (recommended)
[services.web]
command = "npm run dev"
port = 3000

[services.api]
command = "npm run api"
port = 8080

The [services] section gives you more flexibility — custom service names and explicit commands.

[services]

Service definitions. Each [services.{name}] block defines a long-running process.

FieldTypeRequiredDescription
commandstringYesShell command to execute
portintegerYesPort number (1024-65535)
pathstringNoDefault path to append to URL (e.g., “/api/health”)
cwdstringNoWorking directory (default: workspace root)
envtableNoEnvironment variables for this service

Example with multiple services:

[services.frontend]
command = "npm run dev"
port = 5173
path = "/"

[services.backend]
command = "npm run server"
port = 3000
path = "/api"

[services.worker]
command = "npm run worker"
port = 3001
env = { REDIS_URL = "redis://localhost:6379" }

Service Lifecycle:

Services spawn when the colony transitions to running. Each service runs in a dedicated Erlang port owned by a supervisor process. The supervisor:

  1. Spawns the service process
  2. Monitors OS PID via erlang:port_info(Port, os_pid)
  3. Registers Caddy route for the port (e.g., frontend-5173.colony.local)
  4. Streams stdout/stderr to RingLogger

When a service crashes, the supervisor attempts restart (configurable via restart_strategy).

[terminal]

Terminal session configuration for Bloom’s terminal tab.

FieldTypeRequiredDescription
commandstringNoShell command (default: /bin/bash)
envtableNoEnvironment variables for terminal

Example:

[terminal]
command = "zsh"
env = { PS1 = "colony> " }

The terminal command runs in a PTY session managed by Mycelium’s pty_session actor. Access it via Bloom’s terminal tab.

Caddy Routing

Colony automatically registers Caddy routes for each service. URL pattern:

{colony-name}-{port}.colony.local

For example:

  • Service web on port 4001 → hello-colony-4001.colony.local
  • Service api on port 4002 → hello-colony-4002.colony.local

Caddy handles TLS termination (self-signed cert for .local domains) and proxies requests to the service’s port in the colony’s network namespace.

DNS Setup

Run ./scripts/setup-dns.sh to configure dnsmasq for *.colony.local resolution. Without it, you’ll need to access services via localhost:{port} with manual port forwarding.

Template Library

Colony ships with built-in templates:

web-app

Single-page application with frontend framework (React, Vue, Svelte, SolidJS).

[colony]
template = "web-app"

[colony.environment]
node_version = "22"
package_manager = "bun"

[services.web]
command = "npm run dev"
port = 5173

api-service

Backend API server (Express, Fastify, Hono).

[colony]
template = "api-service"

[colony.environment]
node_version = "22"

[services.api]
command = "npm start"
port = 3000
path = "/api"

full-stack

Combined frontend + backend.

[colony]
template = "full-stack"

[colony.environment]
node_version = "22"
package_manager = "bun"

[services.web]
command = "npm run dev"
port = 5173

[services.api]
command = "npm run server"
port = 3000

python-app

Python application with virtual environment.

[colony]
template = "python-app"

[colony.environment]
python_version = "3.12"

[services.web]
command = "python -m uvicorn main:app --host 0.0.0.0 --port 8000"
port = 8000

Creating Custom Templates

Store custom templates in ~/.colony/templates/{name}/colony.toml.

Example: rust-api template

# ~/.colony/templates/rust-api/colony.toml
[colony]
template = "rust-api"

[colony.environment]
rust_version = "stable"

[services.api]
command = "cargo run --release"
port = 8080
path = "/api"

Use it:

curl -X POST http://localhost:8000/api/colonies \
  -H "Content-Type: application/json" \
  -d '{
    "name": "my-rust-api",
    "config": {
      "template": "rust-api"
    }
  }'

Environment Variables

Services inherit environment variables from three sources:

  1. System environment (PATH, HOME, etc.)
  2. Colony-level variables ([colony.env])
  3. Service-level variables ([services.{name}.env])

Example:

[colony.env]
DATABASE_URL = "postgres://localhost:5432/colony"

[services.api]
command = "npm start"
port = 3000
env = { PORT = "3000", LOG_LEVEL = "debug" }

The api service gets:

  • DATABASE_URL from colony-level
  • PORT and LOG_LEVEL from service-level
  • All system variables

Validation

Colony validates colony.toml on creation:

  • Port conflicts — No two services can use the same port
  • Invalid commands — Commands must be non-empty strings
  • Missing runtimes — Requested runtime versions must be available
  • Name conflicts — Colony names must be unique

Validation errors come back immediately via the API.

Next Steps

Templates are the foundation of reproducible, isolated development environments. Master them to unlock Colony’s full potential.