Compose
Manage your Arctic cluster using YAML configuration files
This guide explains how to use the arctic compose command to manage your cluster declaratively using YAML configuration files.
Configuration Builder
Use the Configuration Builder to visually define peers and services, then export a ready-to-use cluster.yaml.
Overview
The compose command is the recommended way to deploy and manage Arctic clusters. It provides an Infrastructure as Code (IaC) approach where you define your desired cluster state in a YAML file and apply it declaratively.
Benefits
- Version Control: Track cluster configuration changes in Git
- Reproducibility: Deploy identical configurations across environments
- Review Process: Use pull requests to review changes before applying
- Automation: Integrate with CI/CD pipelines
When to Use Compose vs Imperative Commands
| Scenario | Recommended Approach |
|---|---|
| Initial cluster setup | compose apply |
| Managing multiple environments | compose apply |
| Production deployments | compose apply |
| CI/CD deployments | compose apply |
| Quick one-off changes | Imperative commands |
| Debugging/exploration | Imperative commands |
Basic Workflow
The typical workflow for using compose is:
- Init a starter configuration (for new clusters)
- Export existing configuration (if upgrading from imperative management)
- Edit the YAML file to define desired state
- Format the configuration for consistency
- Validate the configuration
- Diff to preview changes
- Apply to make changes
# Create starter config (new clusters)
arctic compose init --file cluster.yaml
# Or export existing state (existing clusters)
arctic compose export --file cluster.yaml
# Edit cluster.yaml as needed
# Format after editing (optional but recommended)
arctic compose fmt cluster.yaml --write
# Validate the config
arctic compose validate cluster.yaml
# Preview changes
arctic compose diff cluster.yaml
# Apply changes
arctic compose apply cluster.yamlConfiguration File Structure
The schema version is v1. The top-level keys, in canonical order, are
version, license, requires, server, peers, and services.
Minimal Example
version: v1
peers:
- name: agent-1
endpoints:
- 192.168.1.10:8080
- name: agent-2
endpoints:
- 192.168.1.20:8080
services:
- name: tunnel-1-to-2
source_peer: agent-1
target_peer: agent-2
transport_type: tcp
routes:
- source_cidr: 0.0.0.0/0
dest_cidr: 10.0.0.0/8
priority: 100A service needs transport_type and either an interface block or routes to
route traffic.
Peers cannot be deleted via compose
Removing a peer from the configuration file does not remove it from the
cluster. Use arctic peers delete (or the API) to remove peers.
Full Example
version: v1
license: ./license.json
requires:
agent: "^v1.4.0"
server:
peer: datacenter-west
fallback_peer: datacenter-east
peers:
- name: datacenter-west
description: Primary west DC agent
type: agent
api_access: full
endpoints:
- https://west.internal:8080
- 10.0.1.10:8080
- name: datacenter-east
type: agent
endpoints:
- 10.0.2.10:8080
services:
- name: west-to-east
source_peer: datacenter-west
target_peer: datacenter-east
transport_type: tcp
fully_transparent: true
interface:
enabled: true
vlan_id: 100
ipv4: 10.100.0.1/24
mac: auto
qos:
bandwidth_limit_mbps: 100
default_rtt_ms: 30
disable_auto_rtt: false
memlimit_cap_mb: 64
routes:
- source_cidr: 0.0.0.0/0
dest_cidr: 10.0.2.0/24
priority: 100
- name: east-to-west
source_peer: datacenter-east
target_peer: datacenter-west
transport_type: tcp
fully_transparent: true
routes:
- source_cidr: 0.0.0.0/0
dest_cidr: 10.0.1.0/24
priority: 100Top-level Fields
| Field | Required | Description |
|---|---|---|
version | yes | Schema version. Currently v1. |
license | no | Relative path to the license file (resolved from the config file location). Used for bootstrap when all peers are unbootstrapped. |
requires | no | Version constraints every peer must satisfy. See below. |
server | no | Designated server peer for centralized operations. Absent means fully decentralized. See below. |
peers | yes | Peers (nodes) in the cluster. |
services | yes | Services (tunnels) between peers. |
requires
Declares version constraints that every peer in the cluster must satisfy. The constraint is checked after pre-flight connectivity and before diff or apply.
requires:
agent: "^v1.4.0"| Syntax | Meaning |
|---|---|
vX.Y.Z | Exact version. |
~vX.Y.Z | Patch range (any vX.Y.* at or above the given patch). |
^vX.Y.Z | Minor range (any vX.* at or above the given minor). |
server
When present, apply targets this peer (with an optional single fallback) for all authenticated requests. Omitting the block keeps Arctic in fully decentralized mode.
server:
peer: datacenter-west
fallback_peer: datacenter-east
features:
webui: false
stun: false| Field | Required | Description |
|---|---|---|
peer | yes | Name of the primary server peer. Must reference a peer in peers. |
fallback_peer | no | Single fallback used when the primary is unreachable at connection level. This is a recovery path, not a load balancer; lock state held by the primary is not visible from the fallback. |
features.webui | no | Reserved in v1. |
features.stun | no | Reserved in v1. |
peers
Each peer is a node in the cluster. Canonical field order: name,
description, type, api_access, address (deprecated), endpoints.
| Field | Required | Description |
|---|---|---|
name | yes | Unique human-readable identifier. |
description | no | Free-form description shown in operator output. Not gossiped, no routing effect. |
type | no | Peer role: agent (default) or server. |
api_access | no | API exposure: full (default) or internal. Internal-only peers reject user-facing endpoints and are reachable via the agent's recovery token only. |
endpoints | yes | Ordered list of addresses. The CLI tries them in order; the first to respond wins. Each entry may be a bare host (10.0.0.2), host:port (10.0.0.2:9090), or a full URL (https://node-a.internal). |
address is deprecated
The legacy single-value address field still parses, but new configs should
use endpoints. If both are set, address is ignored and the linter emits a
deprecation warning.
services
Each service is a tunnel between two peers. Canonical field order: name,
source_peer, target_peer, transport_type, fully_transparent,
interface, qos, routes.
| Field | Required | Description |
|---|---|---|
name | yes | Unique human-readable identifier. |
source_peer | yes | Peer name where the service originates. |
target_peer | yes | Peer name where the service terminates. |
transport_type | yes | Tunnel protocol: tcp or kcp. |
fully_transparent | no | Enables fully transparent proxying mode. |
interface | no | MACVLAN interface configuration. See below. |
qos | no | Quality of service settings. See below. |
routes | no | Policy / CIDR routing rules. See below. |
A service uses one of two routing modes: a MACVLAN interface
(interface.enabled: true) or policy/CIDR routes.
interface
Canonical field order: enabled, vlan_id, ipv4, mac.
| Field | Required | Description |
|---|---|---|
enabled | yes | Enables MACVLAN interface creation for this service. |
vlan_id | no | IEEE 802.1Q VLAN tag, 1-4094. Omit (or 0) for no VLAN tagging. |
ipv4 | no | Desired IPv4 address in CIDR notation. Omit for DHCP. |
mac | no | Hardware address. "" lets the agent pick at runtime (may change across recreations); auto derives a deterministic MAC from cluster ID, source peer, and service name; or an explicit colon-separated lowercase hex MAC (aa:bb:cc:dd:ee:ff). |
qos
Canonical field order: bandwidth_limit_mbps, default_rtt_ms,
disable_auto_rtt, memlimit_cap_mb.
| Field | Required | Description |
|---|---|---|
bandwidth_limit_mbps | no | Maximum bandwidth in Mbps. 0 disables shaping and RTT probing for this link. |
default_rtt_ms | no | Seed round-trip time in milliseconds used to tune shaping (0 uses the system default). |
disable_auto_rtt | no | Disables RTT latency probing for this link when true. |
memlimit_cap_mb | no | Caps the calculated shaper memlimit in megabytes (0 disables the cap). Use on memory-constrained hosts. |
routes
Canonical field order: source_cidr, dest_cidr, priority.
| Field | Required | Description |
|---|---|---|
source_cidr | no | Source network in CIDR notation. |
dest_cidr | no | Destination network in CIDR notation. |
priority | yes | Tie-breaker order. A lower value is higher priority. |
Routes are evaluated by specificity first (MACVLAN interface match > source+dest
CIDR > source CIDR > dest CIDR), and priority only breaks ties between routes
of equal specificity.
Validating Configuration
Always validate your configuration before applying:
arctic compose validate cluster.yamlValidation runs parse, then schema validation, then lint. Lint warnings pass
by default; pass --strict to treat warnings as errors. The command supports
--json for machine-readable output and --quiet to show errors only.
Previewing Changes
Use diff to see what will change before applying:
arctic compose diff cluster.yamlThe diff legend is:
+create-delete (shown with--prune)~modify
Applying Configuration
Basic Apply
arctic compose apply cluster.yamlThis will:
- Run pre-validation (lint)
- Check pre-flight connectivity to each peer
- Enforce any
requiresconstraints - Show planned changes
- Prompt for confirmation
- Apply changes
Apply Flags
| Flag | Default | Description |
|---|---|---|
--dry-run | false | Show changes without applying. |
--prune | false | Delete resources not present in the config. |
--ignore-unreachable | false | Skip peers that cannot be contacted. |
--license-file <path> | - | License file used for bootstrap. |
--credentials-file <path> | - | Write bootstrap credentials to a JSON file. |
--env-file <path> | - | Write bootstrap credentials to a .env file. |
--save-config | true | Save the cluster to the CLI config and set it as current. Use --save-config=false to disable. |
--skip-validate | false | Skip pre-validation checks. |
--strict | false | Treat validation warnings as errors. |
--no-lock | false | Skip the cluster-wide apply lock (the local lock still applies). |
--skip-preflight | false | Skip the pre-apply reachability check of peers. |
--skip-requires | false | Skip the requires.agent version check. |
--state-dir <path> | .arctic | Directory for the local state cache, lock, and backups. |
--backup-retention <n> | 5 | Number of state backups to keep (1-100). |
Dry Run
Preview changes without applying:
arctic compose apply cluster.yaml --dry-runPrune Orphaned Resources
Delete services not defined in the configuration:
arctic compose apply cluster.yaml --pruneUse --prune with caution. Review the plan with --dry-run first. Note that
pruning never deletes peers.
Handle Unreachable Peers
Skip peers that cannot be contacted:
arctic compose apply cluster.yaml --ignore-unreachableBootstrap with License
When all peers are unbootstrapped, the first peer is bootstrapped using the
license. Provide the license via --license-file or the top-level license
key, and capture the generated credentials:
arctic compose apply cluster.yaml \
--license-file license.json \
--credentials-file creds.jsonThe credentials file contains the OAuth client ID (cli_...) and secret
(sec_...) for the bootstrapped cluster. Store it securely; the secret cannot
be retrieved later.
Exporting Configuration
Export current cluster state to YAML:
# To stdout
arctic compose export
# To file
arctic compose export --file cluster.yamlThis is useful for:
- Migrating from imperative to declarative management
- Creating backups
- Starting a new configuration from existing state
The license field is left blank on export; fill it in manually before
applying to a fresh cluster.
Best Practices
Use Version Control
Store your configuration files in Git:
git add cluster.yaml
git commit -m "Add west-to-east tunnel service"Review Before Applying
Always use diff and --dry-run before applying to production:
# First, see the diff
arctic compose diff production.yaml
# Then, dry run
arctic compose apply production.yaml --dry-run
# Finally, apply
arctic compose apply production.yamlCredential Management
When using --credentials-file or --env-file:
- Never commit credentials to version control
- Use secure storage (e.g. a secrets manager)
Troubleshooting
Validation Errors
ERROR - Configuration invalid
Errors:
- services[0]: source_peer "unknown" not found in peers listFix: Ensure all peer references match peer names defined in the peers
section.
Connection Errors
Error: failed to connect to peer agent-2: connection refusedOptions:
- Verify the peer endpoints are correct
- Check network connectivity
- Use
--ignore-unreachableto skip
Drift Detection
If the cluster state has drifted from your configuration:
# See differences
arctic compose diff cluster.yaml
# Re-apply to restore desired state
arctic compose apply cluster.yamlSee Also
- CLI Reference - CLI command reference
- Configuration Reference - YAML schema reference