Skip to content

Getting Started

Deploy ComposeFlux and manage Docker Compose stacks via GitOps.

Prerequisites

  • Docker with Compose v2+
  • Git repository with Compose stacks
  • Secrets management: local Age encrypted files (*.age) or a remote provider (Bitwarden / Infisical)
  • SSH key for Git access (store in secrets manager or mount as volume)

Environment Variables

Required

Variable Description
GIT_REPO_URL Git repository SSH URL (e.g., git@github.com:user/repo.git)
STACK_PATH Path to stacks directory in repo (relative to repo root)

Optional - Local Age Encrypted Secrets

Variable Description Default
AGE_PASSPHRASE Passphrase used to decrypt *.age secret files in Git repositories ""

When unset, local secrets are disabled and ComposeFlux does not scan for *.age files.

See the Age Encrypted Secrets Setup Guide for full details on encrypting and layering secrets.

Optional - Remote Secrets Provider

Configure either Bitwarden or Infisical credentials. ComposeFlux selects the provider from the configured credentials. Local and remote secrets can be enabled together, but only one provider may be configured in each category.

Bitwarden:

Variable Description Default
BITWARDEN_ACCESS_TOKEN Machine account access token
BITWARDEN_ORGANIZATION_ID Organization ID
BITWARDEN_PROJECT_ID Project ID
BITWARDEN_API_URL Bitwarden API URL https://vault.bitwarden.com/api
BITWARDEN_IDENTITY_URL Bitwarden Identity URL https://vault.bitwarden.com/identity

Infisical:

Variable Description Default
INFISICAL_CLIENT_ID Universal Auth client ID
INFISICAL_CLIENT_SECRET Universal Auth client secret
INFISICAL_ENVIRONMENT Environment slug (e.g., prod)
INFISICAL_PROJECT_ID Project ID
INFISICAL_SITE_URL Infisical site URL https://app.infisical.com
INFISICAL_SECRET_PATH Secret path in Infisical project. Supports comma-separated paths (e.g., /generic,/apps/prod). If the same secret exists in multiple paths, the last path's value takes precedence. /

Optional

Variable Description Default
GIT_DEPLOY_KEY_SECRET_REF Deploy key secret reference (name or ID) in secrets manager (See Deploy Key Secret Reference)
GIT_SSH_KEY_PATH SSH key path inside container /.ssh/composeflux_id_rsa
GIT_CLONE_PATH Local clone directory /opt/compose-stack
GIT_INTERVAL Git sync interval 5m
HEALTH_RECONCILE_INTERVAL Interval for proactive stack health reconciliation. ComposeFlux redeploys any stack with a non-running container (unless it is a completed init container). Set to e.g. 5m to enable. disabled
IMAGE_UPDATE_SCHEDULE Cron expression for Docker image update checks, e.g. 0 3 * * *. Empty = disabled. ""
GIT_BRANCH Git branch to track main
CONFIG_FILE Stack config file name (see Stack Configuration) stack.yml
LOG_LEVEL Log level (debug/info/warn/error) info
LOG_FORMAT Log format (console/json) console
LOG_ADD_SOURCE Add source location to logs false
REMOVE_ORPHANS Remove orphan containers during deploy true
PRUNE_INTERVAL Interval for periodic Docker resource pruning (images, volumes, build cache). Only runs when all managed stacks are healthy. Set to 0 to disable. 24h

Warning

When PRUNE_INTERVAL is set, pruning removes dangling (untagged) images, unused volumes, and build cache. Containers and networks are not pruned. Pruning requires every discovered source stack to be present and healthy in Docker and none to have composeflux.health.suspend=true in its loaded Git Compose project.

GIT_INTERVAL must be greater than zero. Health and prune intervals must be nonnegative; 0 disables those optional loops. A nonempty IMAGE_UPDATE_SCHEDULE must be a valid cron expression. Invalid values fail startup before clients are initialized.

Commands

ComposeFlux supports two commands:

Usage: composeflux <command> [flags]

A GitOps continuous deployment tool for Docker Compose.

Flags:
  -h, --help                    Show context-sensitive help.
      --log-format="console"    Set the output format of the logs. Must be "console" or "json" ($LOG_FORMAT).
      --log-level=INFO          Set the log level. Must be "DEBUG", "INFO", "WARN" or "ERROR" ($LOG_LEVEL).
      --log-add-source          Whether to add source file and line number to log records ($LOG_ADD_SOURCE).
      --version                 Print version information and exit

Commands:
  run     Run ComposeFlux in daemon mode (continuous reconciliation)
  sync    Perform a one-shot sync and deploy

Run "composeflux <command> --help" for more information on a command.
  • run - Daemon mode with continuous reconciliation (default). Performs an initial sync at startup, then checks the Git repository for changes at configured intervals (default: 5 minutes). An error returned by the initial sync exits the process. Errors during later reconciliation are logged without stopping the daemon.
  • sync - One-shot sync and deploy. Performs an immediate sync and force-reconciles all managed stacks. Useful when you update secrets in your secrets manager without making Git changes. See Change Detection.
# Daemon mode (initial sync at startup, then checks Git every 5 minutes)
composeflux run

# One-shot mode - manually trigger sync
composeflux sync

Important: The run daemon fetches secrets and deploys changes only when Git updates are detected or when a stack is unhealthy/missing. If you update secrets in your secrets manager without making Git changes, run composeflux sync manually to apply updated secrets. See Change Detection.

Deploy ComposeFlux

1. Set up Secrets:

2. Configure Git Access:

3. Create .env file:

# Required - Common
GIT_REPO_URL=git@github.com:user/stacks-repo.git
STACK_PATH=stacks

# Option A: Local Age Encrypted Secrets
AGE_PASSPHRASE=your-secure-passphrase

# Option B: Remote Bitwarden Secrets
# GIT_DEPLOY_KEY_SECRET_REF=aaaaaaa-bbbbb-bbbb-cccc-ddddd
# BITWARDEN_ACCESS_TOKEN=your-access-token
# BITWARDEN_ORGANIZATION_ID=your-org-id
# BITWARDEN_PROJECT_ID=your-project-id

# Option C: Remote Infisical Secrets
# GIT_DEPLOY_KEY_SECRET_REF=SSH_PRIVATE_KEY
# INFISICAL_CLIENT_ID=your-client-id
# INFISICAL_CLIENT_SECRET=your-client-secret
# INFISICAL_ENVIRONMENT=prod
# INFISICAL_PROJECT_ID=your-project-id

4. Create compose.yml:

services:
  composeflux:
    image: ghcr.io/veerendra2/composeflux:latest
    container_name: composeflux
    restart: unless-stopped

    environment:
      # Git Configuration
      GIT_REPO_URL: ${GIT_REPO_URL}
      STACK_PATH: ${STACK_PATH}
      # GIT_INTERVAL: 5m              # Sync interval
      # GIT_BRANCH: main

      # Local Age Encrypted Secrets
      AGE_PASSPHRASE: ${AGE_PASSPHRASE}

      # Remote Secrets - Bitwarden
      # GIT_DEPLOY_KEY_SECRET_REF: ${GIT_DEPLOY_KEY_SECRET_REF}
      # BITWARDEN_ACCESS_TOKEN: ${BITWARDEN_ACCESS_TOKEN}
      # BITWARDEN_ORGANIZATION_ID: ${BITWARDEN_ORGANIZATION_ID}
      # BITWARDEN_PROJECT_ID: ${BITWARDEN_PROJECT_ID}

      # Remote Secrets - Infisical
      # GIT_DEPLOY_KEY_SECRET_REF: ${GIT_DEPLOY_KEY_SECRET_REF}
      # INFISICAL_CLIENT_ID: ${INFISICAL_CLIENT_ID}
      # INFISICAL_CLIENT_SECRET: ${INFISICAL_CLIENT_SECRET}
      # INFISICAL_ENVIRONMENT: ${INFISICAL_ENVIRONMENT}
      # INFISICAL_PROJECT_ID: ${INFISICAL_PROJECT_ID}
      # INFISICAL_SITE_URL: https://app.infisical.com

      # Reconciliation & Pruning
      # HEALTH_RECONCILE_INTERVAL: 5m  # Proactive health check interval (disabled by default)
      # PRUNE_INTERVAL: 24h           # Periodic Docker resource prune interval (24h default, 0 to disable)

      # Logging
      # LOG_LEVEL: info
      # LOG_FORMAT: console           # console or json

    volumes:
      - /var/run/docker.sock:/var/run/docker.sock

      # Optional: Custom SSH known_hosts
      # - ./ssh_known_hosts:/etc/ssh/ssh_known_hosts:ro

      # Optional: Mount local SSH key instead of fetching from secrets manager
      # - ~/.ssh/id_rsa:/.ssh/composeflux_id_rsa:ro

Mount SSH Key

If you prefer to mount your SSH key directly instead of storing it in the secrets manager:

  1. Leave the Bitwarden and Infisical credentials unset
  2. Mount your SSH key to the container at the GIT_SSH_KEY_PATH location (default: /.ssh/composeflux_id_rsa)

Deploy Key Secret Reference

ComposeFlux can fetch your SSH deploy key from the secrets manager during startup, so it can clone private repositories without mounting a local key.

How GIT_DEPLOY_KEY_SECRET_REF works:

  • Requires either complete Bitwarden or Infisical credentials
  • When set to a value (e.g., SSH_PRIVATE_KEY or a Bitwarden secret ID), ComposeFlux fetches that secret from your secrets manager
  • Bitwarden: Uses it as the secret ID to fetch (see Bitwarden Add Secrets)
  • Infisical: Uses it as the secret key name to fetch
  • The fetched content must be your SSH private key
  • When left empty (default), skips fetch and uses mounted key at GIT_SSH_KEY_PATH

Example:

environment:
  GIT_DEPLOY_KEY_SECRET_REF: "" # Disable fetch from secrets manager
  # GIT_SSH_KEY_PATH: /.ssh/composeflux_id_rsa  # Optional: custom path

volumes:
  - ~/.ssh/id_rsa:/.ssh/composeflux_id_rsa:ro

5. Start ComposeFlux:

# Default: Run in daemon mode (continuous reconciliation)
docker compose up -d
docker compose logs -f

Verify Deployment

# Check logs
docker compose logs -f

# List managed stacks (should show containers with composeflux label)
docker ps --filter "label=composeflux.managed=true"

# List all compose projects
docker compose ls