Configuration Reference
Complete reference for all Haloy configuration options.
Configuration Structure
The configuration file supports two modes of operation:
- Single Deployment: If no
targetsare defined, the root configuration acts as the single deployment target. - Multi-Target: If
targetsare defined, the root configuration acts as the Base/Default configuration. Specific targets inherit these defaults and can override them.
Single Deployment Example
name: "my-app"
server: "haloy.yourserver.com"
image: "my-app:latest"
name: "my-app"
server: "haloy.yourserver.com"
image: "my-app:latest"
Multi-Target Example
# Base settings inherited by all targets
image: "my-app:latest"
targets:
prod:
server: "prod.server.com"
replicas: 2
staging:
server: "staging.server.com"
replicas: 1
# Base settings inherited by all targets
image: "my-app:latest"
targets:
prod:
server: "prod.server.com"
replicas: 2
staging:
server: "staging.server.com"
replicas: 1
Global Configuration
These options are only available at the top level of the configuration file.
| Key | Type | Description |
|---|---|---|
targets | object | Define multiple deployment targets (see Multi-Server Deployments) |
images | map | Define named images that can be referenced by targets. Values can be strings or objects. |
secret_providers | object | Configure secret providers (see Secret Providers) |
global_pre_deploy | array | Commands to run once before any deployment starts |
global_post_deploy | array | Commands to run once after all deployments finish |
Secret Providers
secret_providers defines reusable secret sources for from.secret references. Haloy currently supports onepassword and sops, and secret references use the provider:source:key format.
secret_providers:
onepassword:
production-db:
vault: "Production"
item: "Database Credentials"
sops:
production:
file: "./secrets.production.yaml"
env:
- name: "DB_PASSWORD"
from:
secret: "onepassword:production-db:password"
- name: "API_SECRET"
from:
secret: "sops:production:api.secret"
secret_providers:
onepassword:
production-db:
vault: "Production"
item: "Database Credentials"
sops:
production:
file: "./secrets.production.yaml"
env:
- name: "DB_PASSWORD"
from:
secret: "onepassword:production-db:password"
- name: "API_SECRET"
from:
secret: "sops:production:api.secret"
1Password sources support account (optional), vault, and item. SOPS sources support file and optional format (yaml, json, or dotenv). SOPS file paths are resolved relative to the Haloy config file.
Target Configuration
These options define a deployment unit.
- At Root: They serve as the default settings (or the single deployment configuration).
- Under
targets: They override the defaults for that specific target.
| Key | Type | Required | Description |
|---|---|---|---|
name | string | Conditional | Application name. (see Container Name Source) |
preset | string | No | Apply preset configuration (Options: database, service) (see Presets) |
protected | boolean | No | Exclude from --all deployments (default: false) (see Protected Targets) |
naming_strategy | string | No | Container naming: “dynamic” (default) or “static” (see Naming Strategy) |
image | string/object | No | Docker image configuration. A string is shorthand for repository. When omitted or partially specified, repository defaults to the target/app name. (see Image Configuration) |
image_key | string | No | Reference a named image from the top-level images map. Cannot be used together with image. |
server | string | No | Haloy server API URL |
api_token | object | No | API token configuration (see Authentication) |
deployment_strategy | string | No | Deployment strategy: “rolling” (default) or “replace” |
domains | array | No | Domain configuration |
replicas | integer | No | Number of container instances (default: 1) |
port | string/integer | No | Container port to expose (default: “8080”). The port your application listens on inside the container. |
health_check_path | string | No | Health check endpoint (default: ”/“) |
min_ready_seconds | integer | No | Minimum stabilization time after a container becomes healthy before it is considered ready (default: 0) |
env | array | No | Environment variables. Supports ${VAR_NAME} interpolation in value: strings (see Environment Variables) |
volumes | array | No | Volume mounts (see Volumes) |
pre_deploy | array | No | Commands to run before deploy (per target) |
post_deploy | array | No | Commands to run after deploy (per target) |
network | string | No | Docker network for the container. (default: haloy) |
Readiness Stabilization
Use min_ready_seconds to require a container to stay healthy for a minimum period after it first passes health checks. This helps catch containers that start successfully and then crash a few seconds later.
name: "my-app"
health_check_path: "/health"
min_ready_seconds: 10
name: "my-app"
health_check_path: "/health"
min_ready_seconds: 10
- Default:
0(disabled) - Valid range:
0to600 - YAML/TOML field name:
min_ready_seconds - JSON field name:
minReadySeconds
Haloy measures this window from the container start time. If the container has already been running long enough when health checks pass, it becomes ready immediately. Otherwise Haloy waits the remaining time and re-checks the container state before finishing the deployment.
Multi-Target Inheritance
Like other target settings, min_ready_seconds can be defined at the root and overridden per target:
min_ready_seconds: 5
targets:
web:
server: "prod.haloy.com"
image: "nginx:latest"
domains:
- domain: "app.example.com"
api:
server: "api.haloy.com"
image: "node:20"
min_ready_seconds: 15
domains:
- domain: "api.example.com"
min_ready_seconds: 5
targets:
web:
server: "prod.haloy.com"
image: "nginx:latest"
domains:
- domain: "app.example.com"
api:
server: "api.haloy.com"
image: "node:20"
min_ready_seconds: 15
domains:
- domain: "api.example.com"
Configuration File Discovery
Haloy automatically searches for configuration files in the current directory in this order:
haloy.yamlhaloy.ymlhaloy.jsonhaloy.toml
Specify a custom location:
# Specific file
haloy deploy --config /path/to/config.yaml
# Directory (will search for config files inside)
haloy deploy --config /path/to/directory
# Specific file
haloy deploy --config /path/to/config.yaml
# Directory (will search for config files inside)
haloy deploy --config /path/to/directory
Most Haloy commands require a configuration file. See Basic Configuration to get started.
Presets
Apply sensible defaults for specific use cases using the preset option.
Database Preset
Use preset: "database" for stateful database services. This automatically applies:
deployment_strategy: "replace"(safer for databases)naming_strategy: "static"(predictable container names)protected: true(prevents accidental deployment)image.history.strategy: "none"(no rollback history)
postgres:
preset: database
image: "postgres:18"
volumes:
- /data/postgres:/var/lib/postgresql/data
postgres:
preset: database
image: "postgres:18"
volumes:
- /data/postgres:/var/lib/postgresql/data
For practical multi-database and backup examples, see Databases.
Service Preset
Use preset: "service" for stateful services that share similar requirements to databases (static naming, replace strategy) but do not need the strict safety of being protected. This automatically applies:
deployment_strategy: "replace"naming_strategy: "static"image.history.strategy: "none"
redis:
preset: service
image: "henrygd/beszel:latest"
redis:
preset: service
image: "henrygd/beszel:latest"
Protected Targets
Prevent accidental deployment of critical services (like databases) by marking them as protected.
targets:
postgres:
protected: true
targets:
postgres:
protected: true
- Skipped by default when using
haloy deploy --all - Deploy explicitly:
haloy deploy -t postgres - Force inclusion:
haloy deploy --all --include-protected
Container Name Source
- Single-target configs: Container names are derived from the root
namefield (required). - Multi-target configs: Container names are derived from the target key by default. Targets can explicitly set
nameto override this.
Example (multi-target):
targets:
production: # Container: production-abc123
server: prod.server.com
staging: # Container: staging-abc123
server: staging.server.com
targets:
production: # Container: production-abc123
server: prod.server.com
staging: # Container: staging-abc123
server: staging.server.com
Constraints for Static Naming:
- Requires
deployment_strategy: "replace" - Does not support multiple replicas
naming_strategy: static
deployment_strategy: replace
naming_strategy: static
deployment_strategy: replace
Naming Strategy
Control how container names are generated.
| Strategy | Description | Example |
|---|---|---|
dynamic (default) | Includes deployment ID | myapp-abc123 |
static | Uses app name only | myapp |
Constraints for Static Naming:
- Requires
deployment_strategy: "replace" - Does not support multiple replicas
name: postgres
naming_strategy: static
deployment_strategy: replace
name: postgres
naming_strategy: static
deployment_strategy: replace
Deployment Strategies
Rolling Deployment (Default)
Gradually replaces old containers with new ones, ensuring zero downtime:
name: "my-app"
deployment_strategy: "rolling"
replicas: 3
name: "my-app"
deployment_strategy: "rolling"
replicas: 3
Replace Deployment
Stops all old containers before starting new ones:
name: "my-app"
deployment_strategy: "replace"
replicas: 3
name: "my-app"
deployment_strategy: "replace"
replicas: 3
Deployment Hooks
Important: All deployment hooks (pre_deploy, post_deploy, global_pre_deploy, and global_post_deploy) are executed by the haloy CLI tool on the machine where you run the deployment command, not on the server. This means:
- Hooks run in your local environment with access to your local filesystem, environment variables, and installed tools
- They do not have direct access to the server’s filesystem or environment
- Use hooks for local preparation tasks like building assets, running tests, or sending notifications
- For server-side operations, consider using container initialization scripts or application startup logic
Run custom commands before or after deployment:
name: "my-app"
pre_deploy:
- "npm run migrate"
- "echo 'Starting deployment'"
post_deploy:
- "curl -X POST https://status.example.com/notify"
- "echo 'Deployment complete'"
name: "my-app"
pre_deploy:
- "npm run migrate"
- "echo 'Starting deployment'"
post_deploy:
- "curl -X POST https://status.example.com/notify"
- "echo 'Deployment complete'"
For multi-target deployments, use global hooks:
global_pre_deploy:
- "npm run build"
- "docker build -t my-app ."
global_post_deploy:
- "echo 'All deployments complete'"
targets:
production:
server: prod.haloy.com
pre_deploy:
- "npm run test:production"
staging:
server: staging.haloy.com
pre_deploy:
- "npm run test:staging"
global_pre_deploy:
- "npm run build"
- "docker build -t my-app ."
global_post_deploy:
- "echo 'All deployments complete'"
targets:
production:
server: prod.haloy.com
pre_deploy:
- "npm run test:production"
staging:
server: staging.haloy.com
pre_deploy:
- "npm run test:staging"
Multi-Target Overrides
When using multi-target deployments, each target can override base configuration:
| Key | Type | Description |
|---|---|---|
server | string | Override the server for this target |
api_token | object | Override the API token configuration |
image | string/object | Override image configuration (repository, tag, etc.) |
image_key | string | Select a named image from top-level images |
domains | array | Override domain configuration |
env | array | Override environment variables |
replicas | integer | Override number of replicas |
preset | string | Override preset |
protected | boolean | Override protected status |
naming_strategy | string | Override naming strategy |
deployment_strategy | string | Override deployment strategy |
port | string | Override container port |
health_check_path | string | Override health check path |
min_ready_seconds | integer | Override readiness stabilization time |
volumes | array | Override volume mounts |
pre_deploy | array | Override pre-deploy hooks |
post_deploy | array | Override post-deploy hooks |
network | string | Override docker network |
Inheritance Rules:
- Base configuration provides defaults for all targets
- Most target-specific values completely override base values (no merging)
- Only specified fields in targets override the base
- Exception: Environment variables (
env) are merged - target values override base values with the same name, while preserving other base variables global_pre_deployandglobal_post_deployrun once regardless of targets
Example
# Base configuration - inherited by all targets
image:
repository: "ghcr.io/my-org/my-app"
tag: "latest"
replicas: 2
port: "8080"
env:
- name: "LOG_LEVEL"
value: "info"
# Targets
targets:
production: # Inherits: replicas=2, port="8080", base env (LOG_LEVEL) # Overrides: image.tag, adds domains, adds/overrides env variables
server: "prod.haloy.com"
image:
tag: "v1.2.3"
domains:
- domain: "my-app.com"
env: # Merges with base env
- name: "NODE_ENV"
value: "production"
# LOG_LEVEL="info" is inherited from base
staging: # Inherits: image (repository + tag), replicas=2, port="8080", base env # Overrides: server, changes replicas, adds domains, overrides LOG_LEVEL
server: "staging.haloy.com"
replicas: 1
domains:
- domain: "staging.my-app.com"
env:
- name: "LOG_LEVEL"
value: "debug"
# FEATURE_FLAG is inherited from base
# Base configuration - inherited by all targets
image:
repository: "ghcr.io/my-org/my-app"
tag: "latest"
replicas: 2
port: "8080"
env:
- name: "LOG_LEVEL"
value: "info"
# Targets
targets:
production: # Inherits: replicas=2, port="8080", base env (LOG_LEVEL) # Overrides: image.tag, adds domains, adds/overrides env variables
server: "prod.haloy.com"
image:
tag: "v1.2.3"
domains:
- domain: "my-app.com"
env: # Merges with base env
- name: "NODE_ENV"
value: "production"
# LOG_LEVEL="info" is inherited from base
staging: # Inherits: image (repository + tag), replicas=2, port="8080", base env # Overrides: server, changes replicas, adds domains, overrides LOG_LEVEL
server: "staging.haloy.com"
replicas: 1
domains:
- domain: "staging.my-app.com"
env:
- name: "LOG_LEVEL"
value: "debug"
# FEATURE_FLAG is inherited from base
Custom Docker Network
By default, containers are attached to the haloy network. You can override this:
name: "my-app"
network: "my-custom-network"
name: "my-app"
network: "my-custom-network"
Important: If you override the default network (haloy), the Haloy proxy will not be able to discover your containers. This means:
- Your application containers will not be included in the proxy routing
- Traffic will not be routed to your application
- Your domains will not work, even though the containers are running
- The containers will be completely ignored by the routing system after they start
Only change the network if you have a specific networking requirement and understand that you’ll need to handle routing separately.
Validation
Validate your configuration file:
haloy validate-config
haloy validate-config --config path/to/config.yaml
haloy validate-config --show-resolved-config # Display with secrets (use with caution)
haloy validate-config
haloy validate-config --config path/to/config.yaml
haloy validate-config --show-resolved-config # Display with secrets (use with caution)
Next Steps
Stay updated on Haloy
Get notified about new docs, deployment patterns, and Haloy updates.