Files
volt/docs/compose.md
Karl Clinger 81ad0b597c Volt CLI: source-available under AGPSL v5.0
Complete infrastructure platform CLI:
- Container runtime (systemd-nspawn)
- VoltVisor VMs (Neutron Stardust / QEMU)
- Stellarium CAS (content-addressed storage)
- ORAS Registry
- GitOps integration
- Landlock LSM security
- Compose orchestration
- Mesh networking

Copyright (c) Armored Gates LLC. All rights reserved.
Licensed under AGPSL v5.0
2026-03-21 00:31:12 -05:00

15 KiB

Voltfile / Constellation Format

A Constellation is the definition of how containers, VMs, services, and resources form a coherent system. volt compose manages Constellations as declarative multi-service stacks — define containers, VMs, services, tasks, networks, and volumes in a single YAML file and deploy them together.

File Discovery

volt compose looks for Constellation definitions in this order:

  1. -f <path> flag (explicit)
  2. volt-compose.yaml in current directory
  3. volt-compose.yml in current directory
  4. Voltfile in current directory (YAML format)

Quick Example

version: "1"
name: web-stack

containers:
  web:
    image: armoredgate/nginx:1.25
    ports:
      - "80:80"
    networks:
      - frontend
    depends_on:
      api:
        condition: service_started

  api:
    image: armoredgate/node:20
    ports:
      - "8080:8080"
    environment:
      DATABASE_URL: "postgresql://app:secret@db:5432/myapp"
    networks:
      - frontend
      - backend

vms:
  db:
    image: armoredgate/ubuntu-24.04
    cpu: 2
    memory: 4G
    networks:
      - backend

networks:
  frontend:
    subnet: 10.20.0.0/24
  backend:
    subnet: 10.30.0.0/24
    internal: true

Deploy:

volt compose up -d        # Create and start in background
volt compose ps           # Check status
volt compose logs -f      # Follow all logs
volt compose down         # Tear down

Top-Level Keys

Key Type Required Description
version string Yes File format version. Currently "1".
name string No Stack name. Used as prefix for workload names.
description string No Human-readable description.
containers map No Container definitions (Voltainer).
vms map No VM definitions (Voltvisor).
services map No systemd service definitions.
tasks map No Scheduled task definitions.
networks map No Network definitions.
volumes map No Volume definitions.
configs map No Configuration file references.
secrets map No Secret file references.

Container Definition

containers:
  <name>:
    image: <string>              # Image name (required)
    build:                       # Build configuration (optional)
      context: <path>            # Build context directory
      file: <path>               # Build spec file
    ports:                       # Port mappings
      - "host:container"
    volumes:                     # Volume mounts
      - host_path:container_path[:ro]
      - volume_name:container_path
    networks:                    # Networks to join
      - network_name
    environment:                 # Environment variables
      KEY: value
    env_file:                    # Load env vars from files
      - .env
    depends_on:                  # Dependencies
      other_service:
        condition: service_started|service_healthy|service_completed_successfully
    restart: no|always|on-failure|unless-stopped
    restart_max_retries: <int>   # Max restart attempts (for on-failure)
    resources:
      cpu: "<number>"            # CPU shares/quota
      memory: <size>             # e.g., 256M, 1G
      memory_swap: <size>        # Swap limit
    healthcheck:
      command: ["cmd", "args"]   # Health check command
      interval: <duration>       # Check interval (e.g., 30s)
      timeout: <duration>        # Check timeout
      retries: <int>             # Retries before unhealthy
      start_period: <duration>   # Grace period on start
    labels:
      key: value

Container Example

containers:
  app-server:
    image: armoredgate/node:20
    build:
      context: ./app
      file: build-spec.yaml
    ports:
      - "8080:8080"
    volumes:
      - app-data:/app/data
      - ./config:/app/config:ro
    networks:
      - backend
    environment:
      NODE_ENV: production
      DATABASE_URL: "postgresql://app:${DB_PASSWORD}@db:5432/myapp"
    env_file:
      - .env
      - .env.production
    depends_on:
      db:
        condition: service_healthy
      cache:
        condition: service_started
    restart: on-failure
    restart_max_retries: 5
    resources:
      cpu: "2"
      memory: 1G
      memory_swap: 2G
    healthcheck:
      command: ["curl", "-sf", "http://localhost:8080/health"]
      interval: 15s
      timeout: 3s
      retries: 5

VM Definition

vms:
  <name>:
    image: <string>              # Base image (required)
    cpu: <int>                   # vCPU count
    memory: <size>               # Memory allocation (e.g., 4G)
    disks:                       # Additional disks
      - name: <string>
        size: <size>
        mount: <path>            # Mount point inside VM
    networks:
      - network_name
    ports:
      - "host:vm"
    provision:                   # First-boot scripts
      - name: <string>
        shell: |
          commands to run
    healthcheck:
      command: ["cmd", "args"]
      interval: <duration>
      timeout: <duration>
      retries: <int>
    restart: no|always|on-failure
    tune:                        # Performance tuning
      cpu_pin: [<int>, ...]      # Pin to physical CPUs
      hugepages: <bool>          # Use hugepages
      io_scheduler: <string>     # I/O scheduler

VM Example

vms:
  db-primary:
    image: armoredgate/ubuntu-24.04
    cpu: 4
    memory: 8G
    disks:
      - name: system
        size: 40G
      - name: pgdata
        size: 200G
        mount: /var/lib/postgresql/data
    networks:
      - backend
    ports:
      - "5432:5432"
    provision:
      - name: install-postgres
        shell: |
          apt-get update && apt-get install -y postgresql-16
          systemctl enable postgresql
    healthcheck:
      command: ["pg_isready", "-U", "postgres"]
      interval: 30s
      timeout: 5s
      retries: 3
    restart: always
    tune:
      cpu_pin: [4, 5, 6, 7]
      hugepages: true
      io_scheduler: none

Service Definition

Define systemd services managed by the Constellation:

services:
  <name>:
    unit:
      type: simple|oneshot|forking|notify
      exec: <string>            # Command to run (required)
      user: <string>
      group: <string>
    restart: no|always|on-failure
    networks:
      - network_name
    healthcheck:
      command: ["cmd", "args"]
      interval: <duration>
    resources:
      memory: <size>
    depends_on:
      other_service:
        condition: service_started

Service Example

services:
  cache-redis:
    unit:
      type: simple
      exec: "/usr/bin/redis-server /etc/redis/redis.conf"
      user: redis
      group: redis
    restart: always
    networks:
      - backend
    healthcheck:
      command: ["redis-cli", "ping"]
      interval: 10s
    resources:
      memory: 512M

Task Definition

Define scheduled tasks (systemd timers):

tasks:
  <name>:
    exec: <string>              # Command to run (required)
    schedule:
      on_calendar: <string>     # systemd calendar syntax
      every: <duration>         # Alternative: interval
    environment:
      KEY: value
    user: <string>
    persistent: <bool>          # Run missed tasks on boot

Task Example

tasks:
  db-backup:
    exec: "/usr/local/bin/backup.sh --target db-primary"
    schedule:
      on_calendar: "*-*-* 02:00:00"
    environment:
      BACKUP_DEST: /mnt/backups

  cleanup:
    exec: "/usr/local/bin/cleanup-old-logs.sh"
    schedule:
      every: 6h

Network Definition

networks:
  <name>:
    driver: bridge               # Network driver (default: bridge)
    subnet: <cidr>               # e.g., 10.20.0.0/24
    internal: <bool>             # If true, no external access
    options:
      mtu: <int>                 # MTU (default: 1500)

Network Examples

networks:
  # Public-facing network
  frontend:
    driver: bridge
    subnet: 10.20.0.0/24
    options:
      mtu: 9000

  # Internal only — no external access
  backend:
    driver: bridge
    subnet: 10.30.0.0/24
    internal: true

Volume Definition

volumes:
  <name>:
    driver: local                # Storage driver
    size: <size>                 # Optional size for file-backed volumes

Volume Examples

volumes:
  web-static:
    driver: local

  app-data:
    driver: local
    size: 10G

  pgdata:
    driver: local
    size: 200G

Configs and Secrets

configs:
  <name>:
    file: <path>                 # Path to config file

secrets:
  <name>:
    file: <path>                 # Path to secret file

Example

configs:
  nginx-conf:
    file: ./config/nginx.conf
  app-env:
    file: ./.env.production

secrets:
  db-password:
    file: ./secrets/db-password.txt
  tls-cert:
    file: ./secrets/server.crt
  tls-key:
    file: ./secrets/server.key

Dependency Conditions

When specifying depends_on, the condition field controls when the dependent service starts:

Condition Description
service_started Dependency has started (default)
service_healthy Dependency passes its health check
service_completed_successfully Dependency ran and exited with code 0
depends_on:
  db:
    condition: service_healthy
  migrations:
    condition: service_completed_successfully
  cache:
    condition: service_started

Environment Variable Interpolation

The Constellation definition supports shell-style variable interpolation:

environment:
  DATABASE_URL: "postgresql://app:${DB_PASSWORD}@db:5432/myapp"
  APP_VERSION: "${APP_VERSION:-latest}"

Variables are resolved from:

  1. Host environment variables
  2. .env file in the same directory as the Constellation definition
  3. Files specified in env_file

Unset variables with no default cause an error.

Compose Commands

Lifecycle

# Deploy the Constellation — create and start everything
volt compose up

# Detached mode (background)
volt compose up -d

# Specific Constellation file
volt compose -f production.yaml up -d

# Build images first
volt compose up --build

# Force recreate
volt compose up --force-recreate

# Tear down the Constellation
volt compose down

# Also remove volumes
volt compose down --volumes

Status and Logs

# Stack status
volt compose ps

# All logs
volt compose logs

# Follow logs
volt compose logs --follow

# Logs for one service
volt compose logs api

# Last 50 lines
volt compose logs --tail 50 api

# Resource usage
volt compose top

# Events
volt compose events

Operations

# Start existing (without recreating)
volt compose start

# Stop (without removing)
volt compose stop

# Restart
volt compose restart

# Execute command in a service
volt compose exec api -- node --version

# Pull images
volt compose pull

# Build images
volt compose build

# Validate Constellation
volt compose config

Project Naming

# Override project name
volt compose --project my-project up

# This prefixes all workload names: my-project-web, my-project-api, etc.

Full Example: Production Constellation

# volt-compose.yaml — Production Constellation
version: "1"
name: production
description: "Production web application"

containers:
  web-proxy:
    image: armoredgate/nginx:1.25
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - web-static:/usr/share/nginx/html:ro
    networks:
      - frontend
      - backend
    depends_on:
      app-server:
        condition: service_healthy
    restart: always
    resources:
      cpu: "0.5"
      memory: 256M
    healthcheck:
      command: ["curl", "-sf", "http://localhost/health"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 10s

  app-server:
    image: armoredgate/node:20
    build:
      context: ./app
      file: build-spec.yaml
    environment:
      NODE_ENV: production
      DATABASE_URL: "postgresql://app:${DB_PASSWORD}@db-primary:5432/myapp"
      REDIS_URL: "redis://cache-redis:6379"
    env_file:
      - .env.production
    ports:
      - "8080:8080"
    volumes:
      - app-data:/app/data
    networks:
      - backend
    depends_on:
      db-primary:
        condition: service_healthy
      cache-redis:
        condition: service_started
    restart: on-failure
    restart_max_retries: 5
    resources:
      cpu: "2"
      memory: 1G
    healthcheck:
      command: ["curl", "-sf", "http://localhost:8080/health"]
      interval: 15s
      timeout: 3s
      retries: 5

vms:
  db-primary:
    image: armoredgate/ubuntu-24.04
    cpu: 4
    memory: 8G
    disks:
      - name: system
        size: 40G
      - name: pgdata
        size: 200G
        mount: /var/lib/postgresql/data
    networks:
      - backend
    ports:
      - "5432:5432"
    provision:
      - name: install-postgres
        shell: |
          apt-get update && apt-get install -y postgresql-16
          systemctl enable postgresql
    healthcheck:
      command: ["pg_isready", "-U", "postgres"]
      interval: 30s
      timeout: 5s
      retries: 3
    restart: always
    tune:
      cpu_pin: [4, 5, 6, 7]
      hugepages: true
      io_scheduler: none

services:
  cache-redis:
    unit:
      type: simple
      exec: "/usr/bin/redis-server /etc/redis/redis.conf"
      user: redis
      group: redis
    restart: always
    networks:
      - backend
    healthcheck:
      command: ["redis-cli", "ping"]
      interval: 10s
    resources:
      memory: 512M

  log-shipper:
    unit:
      type: simple
      exec: "/usr/local/bin/vector --config /etc/vector/vector.toml"
    restart: on-failure
    depends_on:
      app-server:
        condition: service_started

tasks:
  db-backup:
    exec: "/usr/local/bin/backup.sh --target db-primary"
    schedule:
      on_calendar: "*-*-* 02:00:00"
    environment:
      BACKUP_DEST: /mnt/backups

  cleanup:
    exec: "/usr/local/bin/cleanup-old-logs.sh"
    schedule:
      every: 6h

networks:
  frontend:
    driver: bridge
    subnet: 10.20.0.0/24
    options:
      mtu: 9000

  backend:
    driver: bridge
    subnet: 10.30.0.0/24
    internal: true

volumes:
  web-static:
    driver: local
  app-data:
    driver: local
    size: 10G

configs:
  nginx-conf:
    file: ./config/nginx.conf

secrets:
  db-password:
    file: ./secrets/db-password.txt
  tls-cert:
    file: ./secrets/server.crt
  tls-key:
    file: ./secrets/server.key

Full Example: Developer Constellation

# volt-compose.yaml — Developer Constellation
version: "1"
name: dev-environment

vms:
  dev-box:
    image: armoredgate/fedora-workstation
    cpu: 4
    memory: 8G
    disks:
      - name: system
        size: 80G
    volumes:
      - ~/projects:/home/dev/projects
    networks:
      - devnet
    ports:
      - "2222:22"
      - "3000:3000"
      - "5173:5173"
    provision:
      - name: dev-tools
        shell: |
          dnf install -y git nodejs rust golang
          npm install -g pnpm

containers:
  test-db:
    image: armoredgate/postgres:16
    environment:
      POSTGRES_PASSWORD: devpass
      POSTGRES_DB: myapp_dev
    volumes:
      - test-pgdata:/var/lib/postgresql/data
    networks:
      - devnet
    ports:
      - "5432:5432"

  mailhog:
    image: armoredgate/mailhog:latest
    networks:
      - devnet
    ports:
      - "1025:1025"
      - "8025:8025"

networks:
  devnet:
    subnet: 10.99.0.0/24

volumes:
  test-pgdata:
    driver: local