haloy-config
Haloy Configuration
Create haloy.yaml configuration files for deploying applications with haloy.
If no Dockerfile exists and the user needs one, inform them:
"No Dockerfile found in this project. Haloy deploys Docker containers, so you'll need a Dockerfile first. Would you like me to create one using the
dockerizeskill?"
How It Works
- Check for Dockerfile - Verify the project can be containerized
- Check for configured haloy servers - Look for existing server configurations
- Detect the project type by examining:
package.jsonwith framework dependencies (Next.js, TanStack Start, Express, etc.)pyproject.toml,requirements.txt,Pipfile(Python/Django/FastAPI)go.mod(Go)Cargo.toml(Rust)Gemfile(Ruby/Rails)composer.json(PHP/Laravel)
- Detect database dependencies (Prisma, Drizzle, pg, SQLAlchemy, etc.)
- Infer defaults for app name, port, and health check path
- Ask the user for server (from configured list or manual entry) and domain
- If database detected, ask if they want self-hosted or external
- Generate haloy.yaml with appropriate configuration
- Provide next steps for validation and deployment
Project Detection
Detect the framework to determine the default port:
| Framework | Indicator | Default Port |
|---|---|---|
| Next.js | next in package.json dependencies |
3000 |
| TanStack Start | @tanstack/react-start in package.json |
3000 |
| Express/Node.js | express in package.json or generic Node |
3000 |
| Vite | vite in package.json |
5173 (dev) / 3000 (prod) |
| Django | django in requirements.txt or pyproject.toml |
8000 |
| FastAPI | fastapi in requirements.txt or pyproject.toml |
8000 |
| Flask | flask in requirements.txt or pyproject.toml |
5000 |
| Go | go.mod present |
8080 |
| Rust | Cargo.toml present |
8080 |
| Ruby/Rails | rails in Gemfile |
3000 |
| PHP/Laravel | laravel in composer.json |
8000 |
| Default | Unknown framework | 8080 |
Also check the Dockerfile for EXPOSE directives which override framework defaults.
Database Detection
Scan for database dependencies to determine if the project uses a database:
| Indicator | Database Type |
|---|---|
@prisma/client, prisma in package.json |
Check schema for provider |
drizzle-orm in package.json |
Check config for driver |
pg, postgres in package.json |
PostgreSQL |
mysql2 in package.json |
MySQL |
better-sqlite3, sqlite3 in package.json |
SQLite (no service needed) |
sequelize, typeorm in package.json |
Check config for dialect |
psycopg2, asyncpg in requirements.txt |
PostgreSQL |
sqlalchemy in requirements.txt |
Check config for driver |
django in requirements.txt |
Check settings for database |
prisma/schema.prisma file exists |
Check provider in datasource block |
Prisma schema example - look for the provider:
datasource db {
provider = "postgresql" // or "mysql", "sqlite"
url = env("DATABASE_URL")
}
Decision Flow
Use defaults unless a critical value is missing. Only ask the user when necessary.
App Name
- First choice:
namefield inpackage.json - Second choice: Project folder name
- Ask if: Name contains invalid characters or is generic (e.g., "app", "project")
Server URL
Check for configured servers in ~/.config/haloy/client.yaml:
# Example client.yaml structure
servers:
example.haloy.dev:
token_env: HALOY_API_TOKEN_example_HALOY_DEV
prod.mycompany.com:
token_env: HALOY_API_TOKEN_PROD
Decision flow:
-
If servers found: Present them as options and let the user choose. The server keys (e.g.,
example.haloy.dev) are the values to use in the config."I found these configured haloy servers. Which one would you like to deploy to?"
- example.haloy.dev
- prod.mycompany.com
- Enter a different server
-
If no servers found: Check if haloy CLI is installed by running
which haloyorhaloy --version- If haloy is installed: Ask for the server URL manually
"What is your haloy server URL? (e.g., haloy.yourserver.com)"
- If haloy is not installed: Inform the user they need to install and configure the CLI first
"No haloy servers configured. To deploy with haloy, you'll need to install the CLI and add a server. See: https://haloy.dev/docs/client-installation"
- If haloy is installed: Ask for the server URL manually
Domain
- Always ask - Required for web applications
- Example prompt: "What domain should this app be accessible at? (e.g., myapp.example.com)"
- If user says "none" or "skip", omit the domains section
Domain Aliases
- Ask after domain: Once the user provides a domain, ask if they want to add any aliases
- Example prompt: "Would you like to add any domain aliases? (e.g., www.myapp.example.com)"
- Common alias:
www.prefix of the main domain - If user says "none" or "skip", omit the aliases
DNS Reminder
After gathering domain information, remind the user to configure DNS:
"Remember to configure DNS with your domain provider. Point your domain(s) to your haloy server's IP address using an A record, or use a CNAME record if your server has a domain name."
Port
- Use framework default from the table above
- Check Dockerfile for
EXPOSEdirective which takes precedence - Ask only if: Multiple ports exposed or no clear default
Health Check Path
- Search for existing endpoints:
/health,/healthz,/api/health,/_health - If found: Use the existing health endpoint
- If not found: Use
/(root path) - Do NOT ask - haloy will use
/by default which works for most apps
Database
If database dependencies are detected (see Database Detection section):
-
Ask the user:
"I detected database dependencies ([detected type]). Would you like to add a self-hosted database service, or will you use an external provider (Supabase, Neon, RDS, etc.)?"
- Add self-hosted database
- Use external provider (I'll configure DATABASE_URL myself)
-
If self-hosted:
- Generate multi-target config with database service + app target
- Use the
databasepreset for the database target - Add appropriate environment variables with placeholder values
- Add volume for data persistence
- Database defaults by type:
Database Image Port Volume Path PostgreSQL postgres:175432 /var/lib/postgresql/dataMySQL mysql:83306 /var/lib/mysql -
If external provider:
- Generate single-target config as usual
- Remind user: "Remember to set the
DATABASE_URLenvironment variable for your external database."
Advanced Features
Do NOT proactively offer these. Only include them when the user specifically mentions the topic:
- Secret providers (1Password): When the user mentions 1Password, secret management, or wants to avoid plaintext secrets. See
references/config-reference.mdfor setup syntax. - Registry auth / private images: When the user mentions GHCR, private Docker registry, ECR, or similar. See image configuration in
references/config-reference.md. - Deploy hooks: When the user mentions running migrations, notifications, or pre/post deployment commands. See deploy hooks in
references/config-reference.md. - Protected targets: When the user has databases or stateful services they want excluded from
haloy deploy --all. - Naming strategy: Only relevant when
staticis needed (rare). Thedatabaseandservicepresets set this automatically.
Configuration Reference
Haloy searches for haloy.yaml, haloy.yml, haloy.json, or haloy.toml (in that order). JSON uses camelCase keys (e.g., healthCheckPath), YAML/TOML use snake_case.
Haloy supports two modes:
- Single Deployment: No
targetsdefined, root configuration is the deployment target - Multi-Target:
targetsdefined, root acts as base/default configuration
Minimal Configuration (Single Deployment)
name: "my-app"
server: "haloy.yourserver.com"
With Domain and Port
name: "my-app"
server: "haloy.yourserver.com"
domains:
- domain: "my-app.example.com"
aliases:
- "www.my-app.example.com"
port: "3000"
health_check_path: "/health"
With Environment Variables
name: "my-app"
server: "haloy.yourserver.com"
domains:
- domain: "my-app.example.com"
port: "3000"
env:
- name: "NODE_ENV"
value: "production"
- name: "DATABASE_URL"
from:
env: "PRODUCTION_DATABASE_URL"
- name: "API_SECRET"
from:
secret: "onepassword:app-secrets.api-key"
Env var forms: value (inline string), from.env (reads from deployer's local env), from.secret (reads from a secret provider). See references/config-reference.md for all forms including build_arg and interpolation.
With Volumes (for persistent data)
name: "my-app"
server: "haloy.yourserver.com"
domains:
- domain: "my-app.example.com"
volumes:
- "app-data:/app/data"
- "/var/backups/my-app:/app/backups:ro"
Named volumes (e.g., app-data:/app/data) are recommended. Bind mounts require absolute paths on the host side. Modifiers: :ro (read-only), :rw (default), :z/:Z (SELinux).
With Self-Hosted Database (Multi-Target)
server: "haloy.yourserver.com"
env:
- name: POSTGRES_USER
value: "postgres"
- name: POSTGRES_PASSWORD
value: "change-me-in-production"
- name: POSTGRES_DB
value: "my_app"
targets:
postgres:
preset: database
image:
repository: postgres:17
port: 5432
volumes:
- postgres-data:/var/lib/postgresql/data
my-app:
domains:
- domain: "my-app.example.com"
port: 3000
env:
- name: DATABASE_URL
value: "postgres://postgres:change-me-in-production@postgres:5432/my_app"
- name: NODE_ENV
value: "production"
Multi-Target Example
# Base settings inherited by all targets
image:
repository: "my-app"
tag: "latest"
port: "3000"
targets:
production:
server: "prod.haloy.com"
domains:
- domain: "my-app.com"
replicas: 2
staging:
server: "staging.haloy.com"
domains:
- domain: "staging.my-app.com"
replicas: 1
Full Reference
For all configuration options, see: https://haloy.dev/docs/configuration-reference
For exhaustive syntax and advanced features, see: references/config-reference.md
Identity & Deployment:
name- Application name (required for single deployment)server- Haloy server URL (required)targets- Multiple deployment targets (multi-target mode)preset- "database" or "service" (sets strategy, naming, protected defaults)deployment_strategy- "rolling" (default) or "replace"naming_strategy- "dynamic" (default) or "static"protected- Skip target onhaloy deploy --all(default: false)replicas- Number of container instances (default: 1)
Image:
image- String shorthand ("nginx:alpine") or object withrepository,tag,build,registry,source,history,build_configimages- Named image map for multi-target configs
Networking:
domains- Array of domain objects withdomainand optionalaliasesport- Container port (default: "8080")health_check_path- Health check endpoint (default: "/")
Environment & Secrets:
env- Array of env vars:value,from.env,from.secret,build_argsecret_providers- 1Password integration (top-level only)
Storage:
volumes- Named volumes or bind mounts with optional modifiers
Lifecycle Hooks:
pre_deploy/post_deploy- Per-target commands on local machineglobal_pre_deploy/global_post_deploy- Run once across all targets (top-level only)
Multi-Target:
- Root-level fields act as defaults inherited by all targets
- Target-specific fields override root defaults
- Target env vars merge with root env vars (duplicates overridden)
Output Format
After gathering information, create the haloy.yaml file and provide:
- The haloy.yaml file - Written to the project root
- Update .dockerignore - Add
haloy.yamlto.dockerignoreif not already present. This improves Docker layer caching since changes tohaloy.yamlwon't invalidate the build cache. - Validation command:
haloy validate-config - Deployment command:
haloy deploy - If haloy CLI is not installed, show installation options:
# Shell script curl -fsSL https://sh.haloy.dev/install-haloy.sh | sh # Homebrew brew install haloydev/tap/haloy # npm/pnpm/bun npm install -g haloy pnpm add -g haloy bun add -g haloy
Example Interactions
See references/examples.md for detailed interaction examples covering:
- Deploying with configured servers
- Database detection and self-hosted database setup
- Handling missing server configuration
- Using secret providers (1Password)
- Private registry with deploy hooks
See references/config-reference.md for exhaustive syntax on all advanced features.