deeplo
Reference

Configuration

Complete YAML configuration reference — hosts, repos, projects, triggers, and path matching.

Configuration is a single YAML file, by default at /etc/deeplo/config.yml. The default path can be overridden with deeplo check --config or the DEEPLO_CONFIG_FILE env var.

Defaults prefer .yml, but explicit .yaml paths and compose file names still work when you configure them directly.

The YAML file contains only structural deployment config: which hosts exist, which repos to track, and which projects to deploy. Runtime settings (SSH keys, data directory, listen addresses, GitHub tokens, secrets) are configured exclusively via environment variables — see Bootstrap config.

Validate any config before deploying:

deeplo check --config /path/to/config.yml

Top-level structure

version: 1
hosts: [...]
repos: [...]
projects: [...]
FieldTypeRequiredDescription
versionintyesConfig schema version. Must be 1.
hostslistyesOne or more remote Docker hosts.
reposlistyesOne or more git repositories to watch.
projectslistyesOne or more Compose projects to deploy.

hosts

Each entry is a remote host where Docker Compose stacks run.

hosts:
  - name: web-1
    address: 10.0.0.10
    remote_base_dir: /srv/apps

  - name: web-2
    address: 10.0.0.11
    remote_base_dir: /srv/apps
    user: deploy                # optional per-host override
    port: 2222                  # optional per-host override
    compose_bin: docker-compose   # optional override
FieldTypeRequiredDefaultDescription
namestringyesUnique identifier. Referenced by projects[].targets.
addressstringyesHostname or IP address.
remote_base_dirstringyesBase directory on the host where project directories are created.
userstringnoDEEPLO_SSH_USERSSH username override for this host.
portintnoDEEPLO_SSH_PORTSSH port override for this host.
compose_binstringnodockerdocker uses docker compose; docker-compose uses the v1 standalone binary.

repos

Each entry is a git repository to watch. Projects reference repos by name.

repos:
  - name: infra
    url: git@github.com:yourorg/infra.git
    branch: main
    trigger_mode: hybrid
    poll_interval: 60s

  - name: myapp
    url: https://github.com/yourorg/myapp.git
    # branch defaults to "main"
    # trigger_mode defaults to "webhook"
FieldTypeRequiredDefaultDescription
namestringyesUnique identifier. Referenced by projects[].repo.
urlstringyesGit clone URL (HTTPS or SSH).
branchstringnomainBranch to watch.
trigger_modestringnowebhookwebhook, poll, or hybrid. See Triggers.
poll_intervaldurationno60sHow often to poll. Only used when mode is poll or hybrid.

projects

Each entry is a Docker Compose project within a repo.

projects:
  - name: paperless
    repo: infra
    repo_subdir: apps/paperless
    targets:
      - web-1
    # compose_files defaults to [compose.yml]
    # watch_paths defaults to [apps/paperless/**]

  - name: nginx
    repo: infra
    repo_subdir: apps/nginx
    compose_files:
      - compose.yml
    targets:
      - web-1
      - web-2
    watch_paths:
      - apps/nginx/**
    env_files:
      - .env
    remote_dir_name: nginx   # optional; defaults to project name
FieldTypeRequiredDefaultDescription
namestringyesUnique identifier. Used as the remote directory name by default.
repostringyesName of the repo this project belongs to.
repo_subdirstringyesPath within the repo where compose files live (relative to repo root).
compose_fileslist of stringsno[compose.yml]Compose file names, relative to repo_subdir.
targetslist of stringsyesHost names to deploy to (from the hosts list).
watch_pathslist of stringsno["{repo_subdir}/**"]Glob patterns against changed file paths. A deploy fires only if at least one changed file matches.
env_fileslist of stringsno.env file names, relative to repo_subdir. Included in the deploy bundle alongside compose files.
remote_dir_namestringnonameDirectory name under host.remote_base_dir on the target.

Path matching

watch_paths patterns are matched against the full paths of changed files relative to the repo root.

  • * matches any characters within a single path segment
  • ** matches zero or more path segments
  • Standard glob characters (?, [range]) work within a segment
watch_paths:
  - apps/myapp/**          # any file under apps/myapp/
  - shared/lib/**          # also redeploy if shared library changes
  - config/*.yml           # yml files directly in config/

If watch_paths is empty, the project deploys on every push to the repo regardless of which files changed.

If the diff is unavailable (no local mirror yet, or polling without a mirror), the project deploys unconditionally — the unknown diff is treated as "everything changed".

Invalid watch_paths patterns are rejected by deeplo check. Malformed glob syntax does not fall back silently at runtime.


Defaults summary

FieldDefault
hosts[].compose_bindocker
repos[].branchmain
repos[].trigger_modewebhook
repos[].poll_interval60s
projects[].compose_files[compose.yml]
projects[].watch_paths["{repo_subdir}/**"]
projects[].remote_dir_nameproject name

Example: monorepo with multiple projects

All projects live in one repo. Each has its own watch_paths so only the affected project deploys on a given push.

version: 1

hosts:
  - name: vm-1
    address: 10.0.0.10
    remote_base_dir: /srv/apps

  - name: vm-2
    address: 10.0.0.11
    remote_base_dir: /srv/apps

repos:
  - name: infra
    url: git@github.com:yourorg/infra.git
    trigger_mode: hybrid
    poll_interval: 5m

projects:
  - name: api
    repo: infra
    repo_subdir: services/api
    targets: [vm-1, vm-2]
    watch_paths:
      - services/api/**
      - shared/**          # shared code also triggers api

  - name: worker
    repo: infra
    repo_subdir: services/worker
    targets: [vm-1]

  - name: nginx
    repo: infra
    repo_subdir: services/nginx
    targets: [vm-1, vm-2]

Env var interpolation

You can use ${VAR} and ${VAR:-default} in YAML values. This is expanded before parsing:

repos:
  - name: myapp
    url: ${GIT_REPO_URL}
    branch: ${DEPLOY_BRANCH:-main}

Best practices

  • Mount secrets as files and pass their paths via DEEPLO_SSH_KEY_FILE, DEEPLO_GITHUB_WEBHOOK_SECRET_FILE, etc. No secrets belong in the YAML config.
  • Use explicit watch_paths in monorepos. Without them, every push to the repo deploys every project.
  • Keep remote_base_dir consistent across hosts. It simplifies configuration and debugging.
  • Prefer hybrid mode for repos you also watch via webhook. It catches missed webhooks without polling aggressively.
  • Use a dedicated deploy user on each host. Grant it access to the docker group, nothing more.

On this page