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:
repository: "my-app"
tag: "latest"
name: "my-app"
server: "haloy.yourserver.com"
image:
repository: "my-app"
tag: "latest"
Multi-Target Example
# Base settings inherited by all targets
image:
repository: "my-app"
tag: "latest"
targets:
prod:
server: "prod.server.com"
replicas: 2
staging:
server: "staging.server.com"
replicas: 1
# Base settings inherited by all targets
image:
repository: "my-app"
tag: "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 |
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 |
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: “dynamiAc” (default) or “static” (see Naming Strategy) |
image | object | No | Docker image configuration (see Image Configuration) |
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 |
acme_email | string | No | Let’s Encrypt email (required with domains) |
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: ”/“) |
env | array | No | Environment variables (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) |
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:
repository: postgres
tag: "18"
volumes:
- /data/postgres:/var/lib/postgresql/data
postgres:
preset: database
image:
repository: postgres
tag: "18"
volumes:
- /data/postgres:/var/lib/postgresql/data
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:
repository: "henrygd/beszel"
tag: "latest"
redis:
preset: service
image:
repository: "henrygd/beszel"
tag: "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 | object | Override image configuration (repository, tag, etc.) |
domains | array | Override domain configuration |
acme_email | string | Override ACME email |
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 |
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 HAProxy configuration
- 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)