Configuration Reference
Tow uses a single tow.yaml file to define your entire deployment topology. This page covers every configuration option.
File Location
Tow looks for configuration in this order:
- Path specified by
--configflag tow.yamlin the current directorytow.local.yamlis merged on top (for local overrides)
Tip: Add
tow.local.yamlto.gitignore. Use it for developer-specific SSH key paths or custom settings that shouldn’t be shared.
Full Schema
project
Top-level project metadata.
project:
name: my-project # Required. Project name
version: 1.0.0 # Project version
base_dir: /app # Base directory on remote servers (default: /app)
defaults
Global defaults applied to all environments and modules.
defaults:
ssh_user: ec2-user # Default SSH username (default: ec2-user)
ssh_port: 22 # Default SSH port (default: 22)
ssh_key_path: ~/.ssh/id_rsa # Path to SSH private key
deploy_dir: deploy # Deploy directory name (default: deploy)
health_check:
type: tcp # tcp | http | log | command
timeout: 300 # Total timeout in seconds (default: 300)
interval: 5 # Check interval in seconds (default: 5)
retries: 60 # Max retry count (default: 60)
environments
Define target environments with their servers.
environments:
dev:
ssh_user: ubuntu
# ssh_key_path in tow.local.yaml (don't commit secrets)
branch: develop
variables:
LOG_LEVEL: debug
servers:
# Named server — target with: tow deploy -s api-1
- name: api-1
host: 10.0.1.10
modules: [api-server]
labels:
role: api
prod:
# ssh_key_path in tow.local.yaml
branch_policy:
allowed: [main, release/*, hotfix/*]
commands: [deploy, auto, start, restart]
servers:
# Multiple servers with same config → hosts shorthand
- name: api
hosts: [52.78.100.1, 52.78.100.2] # auto-expands to api-1, api-2
modules: [api-server]
# Servers with per-server config → individual entries
- name: kafka-1
host: 10.0.2.1
modules: [kafka]
- name: kafka-2
host: 10.0.2.2
modules: [kafka]
Server naming
Servers are identified by name (recommended) or number (legacy):
servers:
# ✅ Recommended: use name
- name: api-1
host: 10.0.1.10
modules: [api-server]
# ✅ Shorthand: same config for multiple hosts
- name: redis
hosts: [10.0.3.1, 10.0.3.2, 10.0.3.3] # → redis-1, redis-2, redis-3
modules: [redis]
# ⚠️ Legacy: number-based (still supported)
- number: 1
host: 10.0.1.10
Target a specific server by name:
tow deploy -e prod -m kafka -s kafka-2 # by name
tow logs -e prod -m api-server -s api-1 # by name
tow deploy -e prod -m api-server -s 1 # by number (legacy)
Per-server configuration
Use hierarchical config directories with server names:
config/
├── application.yml ← shared defaults
├── prod/
│ └── application.yml ← prod overrides
├── prod-kafka-1/
│ └── server.properties ← kafka-1 specific (broker.id=1)
├── prod-kafka-2/
│ └── server.properties ← kafka-2 specific (broker.id=2)
└── prod-kafka-3/
└── server.properties ← kafka-3 specific (broker.id=3)
Resolution order: config/{env}-{serverName}/ > config/{env}/ > config/
modules
Define deployable services/applications.
modules:
api-server:
type: springboot # Module type (see below)
port: 8080 # Application port
build_cmd: ./gradlew :api-server:bootJar
artifact_path: api-server/build/libs/*.jar
start_cmd: bin/server start # Custom start command
stop_cmd: bin/server stop # Custom stop command
status_cmd: bin/server status # Custom status command
log_path: log/std.log # Log file path (relative to module dir)
deploy_dir: deploy # Override deploy directory
config_dir: config # Config directory for hierarchical resolution
package_includes: # Files/dirs to include in package
- lib/
- bin/
- conf/
data_dirs: # Persistent directories (survive deploys)
- data
- cache
variables: # Module-level variables
JAVA_OPTS: -Xms512m -Xmx1024m
health_check:
type: http # tcp | http | log | command
target: http://localhost:8080/actuator/health
timeout: 120
interval: 3
hooks:
pre_build: ./gradlew test
post_build: echo "Build complete"
pre_deploy: echo "Starting deploy"
post_deploy: "curl -X POST https://hooks.slack.com/services/xxx"
pre_start: ""
post_start: ""
pre_stop: ""
post_stop: ""
ssh: # Per-module SSH override
user: app-deploy
port: 2222
auth: key # key | password | agent
key_path: ~/.ssh/special-key.pem
Module Types
Built-in (Language/Framework)
| Type | Description | Default Build | Default Health Check |
|---|---|---|---|
springboot | Spring Boot applications | ./gradlew :mod:bootJar | HTTP /actuator/health |
java | Generic Java applications | ./gradlew :mod:build | TCP port |
node | Node.js applications | npm ci && npm run build | TCP port |
python | Python applications | pip install -r requirements.txt | TCP port |
go | Go applications | go build -o bin/mod | TCP port |
rust | Rust applications | cargo build --release | TCP port |
php | PHP applications | composer install | TCP port |
ruby | Ruby applications | bundle install | TCP port |
dotnet | C#/.NET applications | dotnet publish | TCP port |
kotlin | Kotlin applications | ./gradlew build | TCP port |
elixir | Elixir applications | mix release | TCP port |
generic | Custom applications | (must specify) | TCP port |
YAML Plugins (Infrastructure — 35 services)
Infrastructure services are loaded from plugins/ directory. No Go code needed.
| Category | Services |
|---|---|
| Databases | MySQL, PostgreSQL, MariaDB, MongoDB, ClickHouse, InfluxDB |
| Message Brokers | Kafka, RabbitMQ, ZooKeeper |
| Kafka Ecosystem | Kafka Connect, Kafka Lag Exporter, KMinion, CMAK |
| Caching & Storage | Redis, Memcached, MinIO, etcd |
| Monitoring | Prometheus, Grafana, Node Exporter, Kibana |
| Logging | Elasticsearch, Logstash, Fluentd, Loki, Promtail |
| Web & Proxy | Nginx, HAProxy |
| Security | Vault, Consul, Keycloak |
| CI/CD | Jenkins, SonarQube |
| Data & Workflow | Airflow, Superset |
Usage example:
modules:
my-db:
type: postgresql # Loaded from plugins/postgresql.yaml
port: 5432
my-cache:
type: redis # Loaded from plugins/redis.yaml
port: 6379
All defaults can be overridden in tow.yaml. See plugins/README.md for full list and plugin authoring guide.
Health Check Types
TCP
Checks if a port is accepting connections.
health_check:
type: tcp
target: ":8080" # Port to check (default: module port)
timeout: 60
interval: 3
HTTP
Sends an HTTP GET and expects a 2xx response.
health_check:
type: http
target: http://localhost:8080/actuator/health
timeout: 120
interval: 5
Log
Watches a log file for a specific pattern.
health_check:
type: log
target: "Started Application in" # Pattern to grep for
timeout: 120
interval: 3
Command
Runs a custom command; success (exit code 0) means healthy.
health_check:
type: command
target: "redis-cli ping | grep PONG"
timeout: 30
interval: 5
Environment Variable Interpolation
Use ${VAR_NAME} syntax anywhere in tow.yaml:
environments:
prod:
servers:
- number: 1
host: ${PROD_SERVER_1} # Resolved from OS environment
- number: 2
host: ${PROD_SERVER_2}
modules:
api-server:
ssh:
password: ${DEPLOY_PASSWORD} # Secrets from environment
Security: Never hardcode secrets in
tow.yaml. Use environment variables ortow.local.yaml(in.gitignore).
Retention Policy
Control how many old deployments are kept on remote servers.
retention:
keep: 5 # Keep the 5 most recent deployments (default: 5)
auto_cleanup: true # Automatically clean up after deploy/auto (default: false)
- The current active deployment is never removed
- Manual cleanup:
tow cleanup -e prod -m api-server --keep 3 - Without
auto_cleanup, old deployments accumulate until you runtow cleanup
Notifications
Send deployment event notifications to Slack, Discord, or any webhook.
notifications:
- type: slack
url: ${SLACK_WEBHOOK_URL}
- type: discord
url: ${DISCORD_WEBHOOK_URL}
- type: webhook
url: https://my-service.com/deploy-events
Events sent: deploy_start, deploy_success, deploy_failed, auto_rollback, rollback_success, rollback_failed.
Webhook payload format:
{
"project": "my-platform",
"environment": "prod",
"module": "api-server",
"event": "deploy_success",
"message": "deployment completed",
"timestamp": "2026-03-26T14:30:00Z"
}
Local Overrides (tow.local.yaml)
tow.local.yaml is deep-merged over tow.yaml. Perfect for:
- Developer-specific SSH keys
- Local port overrides
- Custom build commands for development
# tow.local.yaml — in .gitignore
defaults:
ssh_key_path: ~/.ssh/my-personal-key.pem
environments:
dev:
servers:
- number: 1
host: 192.168.1.100 # Local dev server
Merge rules:
- Scalar values are replaced
- Maps are deep-merged
- Lists are replaced (not appended)
Hierarchical Config Directories
Tow supports hierarchical configuration resolution for your application config files:
config/
├── application.yml ← base (all environments)
├── dev/
│ └── application.yml ← dev environment override
├── prod/
│ └── application.yml ← prod environment override
└── prod-1/
└── application.yml ← prod server-1 specific
Resolution order (highest priority first):
config/{env}-{server_number}/config/{env}/config/
When packaging, Tow copies the resolved config files to the conf/ directory in the deployment archive.
Complete Example
project:
name: my-saas
version: 2.1.0
base_dir: /app
defaults:
ssh_user: ec2-user
ssh_port: 22
ssh_key_path: ~/.ssh/deploy.pem
health_check:
type: tcp
timeout: 120
interval: 5
environments:
dev:
ssh_key_path: ~/.ssh/dev.pem
branch: develop
variables:
SPRING_PROFILES_ACTIVE: dev
servers:
- number: 1
host: 10.0.1.10
staging:
ssh_key_path: ~/.ssh/staging.pem
branch_policy:
allowed: [develop, release/*]
servers:
- number: 1
host: 10.0.2.10
prod:
ssh_key_path: ~/.ssh/prod.pem
branch_policy:
allowed: [main, hotfix/*]
commands: [deploy, auto, start, restart, rollback]
servers:
- number: 1
host: ${PROD_SERVER_1}
modules: [api-server, batch-server]
- number: 2
host: ${PROD_SERVER_2}
modules: [api-server]
- number: 3
host: ${PROD_SERVER_3}
modules: [kafka, redis]
modules:
api-server:
type: springboot
port: 8080
build_cmd: ./gradlew :api-server:bootJar -x test
artifact_path: api-server/build/libs/api-server-*.jar
config_dir: config
data_dirs: [uploads, cache]
health_check:
type: http
target: http://localhost:8080/actuator/health
timeout: 120
hooks:
pre_build: ./gradlew :api-server:test
post_deploy: |
curl -X POST ${SLACK_WEBHOOK} \
-d '{"text":"api-server deployed to prod"}'
batch-server:
type: springboot
port: 8081
build_cmd: ./gradlew :batch-server:bootJar
health_check:
type: log
target: "Started BatchApplication in"
kafka:
type: kafka
port: 9092
data_dirs: [kafka-logs]
redis:
type: redis
port: 6379
data_dirs: [redis-data]