haloy-config

SKILL.md

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 dockerize skill?"

How It Works

  1. Check for Dockerfile - Verify the project can be containerized
  2. Check for configured haloy servers - Look for existing server configurations
  3. Detect the project type by examining:
    • package.json with 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)
  4. Detect database dependencies (Prisma, Drizzle, pg, SQLAlchemy, etc.)
  5. Infer defaults for app name, port, and health check path
  6. Ask the user for server (from configured list or manual entry) and domain
  7. If database detected, ask if they want self-hosted or external
  8. Generate haloy.yaml with appropriate configuration
  9. 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: name field in package.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:

  1. 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
  2. If no servers found: Check if haloy CLI is installed by running which haloy or haloy --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"

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 EXPOSE directive 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):

  1. 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)
  2. If self-hosted:

    • Generate multi-target config with database service + app target
    • Use the database preset 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:17 5432 /var/lib/postgresql/data
    MySQL mysql:8 3306 /var/lib/mysql
  3. If external provider:

    • Generate single-target config as usual
    • Remind user: "Remember to set the DATABASE_URL environment 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.md for 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 static is needed (rare). The database and service presets 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:

  1. Single Deployment: No targets defined, root configuration is the deployment target
  2. Multi-Target: targets defined, 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 on haloy deploy --all (default: false)
  • replicas - Number of container instances (default: 1)

Image:

  • image - String shorthand ("nginx:alpine") or object with repository, tag, build, registry, source, history, build_config
  • images - Named image map for multi-target configs

Networking:

  • domains - Array of domain objects with domain and optional aliases
  • port - Container port (default: "8080")
  • health_check_path - Health check endpoint (default: "/")

Environment & Secrets:

  • env - Array of env vars: value, from.env, from.secret, build_arg
  • secret_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 machine
  • global_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:

  1. The haloy.yaml file - Written to the project root
  2. Update .dockerignore - Add haloy.yaml to .dockerignore if not already present. This improves Docker layer caching since changes to haloy.yaml won't invalidate the build cache.
  3. Validation command:
    haloy validate-config
    
  4. Deployment command:
    haloy deploy
    
  5. 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.

Weekly Installs
14
First Seen
Jan 28, 2026
Installed on
opencode13
gemini-cli13
github-copilot13
codex13
amp12
kimi-cli12