Docker Compose

The Docker Compose plugin enables formae to manage Docker Compose stacks as infrastructure via the docker compose CLI. It supports the full resource lifecycle: create, read, update, delete, and discovery.

Repository: formae-plugin-compose

Installation

git clone https://github.com/platform-engineering-labs/formae-plugin-compose.git
cd formae-plugin-compose
make install

This builds the plugin and installs it to ~/.pel/formae/plugins/compose/. The formae agent discovers installed plugins automatically on startup. Requires Go 1.25+, Pkl CLI, and Docker with Compose v2 plugin.

Configuration

Target

Configure a Docker Compose target in your Forma file:

import "@formae/formae.pkl"
import "@compose/compose.pkl"

target: formae.Target = new formae.Target {
    label = "local-docker"
    namespace = "DOCKER"
    config = new compose.Config {}
}

Config Properties

Property Default Description
host unix:///var/run/docker.sock Docker host URI

Credentials

Docker credentials (registry authentication, TLS certificates) are read from the environment. The plugin uses the standard Docker credential chain, including ~/.docker/config.json and any configured credential helpers.

Supported Resources

Type Description Native ID Discoverable Extractable
Docker::Compose::Stack A Docker Compose project projectName Yes Yes

Docker::Compose::Stack Schema

Field Type Mutability Description
projectName String createOnly Compose project name, used as native ID. Changing triggers replacement.
composeFile String mutable Compose YAML content. Changes trigger docker compose up.
endpoints Mapping<String, String>? read-only (provider default) Port mappings resolved after apply. Maps "service:port" to actual URLs.
status String? read-only Current project status from Docker.

Resolvables

The Compose plugin exposes a StackResolvable that enables cross-plugin references. This is a key feature for multi-plugin workflows: other targets can resolve their configuration from values that only become known after a Compose stack is applied, such as port mappings.

After apply, the endpoints field is populated with the actual URLs (e.g., {"lgtm:3000": "http://localhost:3000"}). Other plugins can reference these endpoints to configure themselves automatically.

import "@compose/compose.pkl"

local lgtmStack = new compose.Stack {
    label = "lgtm"
    projectName = "my-lgtm"
    composeFile = "..."  // services with ports
}

// Another target can resolve from the stack's endpoints
new formae.Target {
    label = "grafana"
    namespace = "GRAFANA"
    config = new Mapping {
        ["Endpoints"] = lgtmStack.res.endpoints
        ["EndpointKey"] = "lgtm:3000"
    }
}

Learn more about resolvables in Res. See Target resolvables for how compose endpoints flow into other target configs. Learn how to build resolvables for your own plugins in Plugin SDK: Resolvables.

Examples

Examples are available in the plugin repository. Clone the repo and resolve Pkl dependencies before running:

git clone https://github.com/platform-engineering-labs/formae-plugin-compose.git
cd formae-plugin-compose
pkl project resolve examples/basic

Available examples:

Example Description
basic Single nginx container
lgtm Grafana LGTM all-in-one observability stack
# Evaluate an example
formae eval examples/basic/main.pkl

# Apply resources
formae apply --mode reconcile --watch examples/basic/main.pkl

The LGTM observability example demonstrates the cross-plugin resolvable pattern: the Compose stack provisions a Grafana LGTM container, and its endpoints are resolved into a Grafana target configuration. See the Grafana plugin for the full pattern.

What's next

  • Res -- learn how resolvables enable cross-resource and cross-plugin references
  • Target resolvables -- understand how compose endpoints flow into other target configs
  • Grafana plugin -- see an example of using compose endpoints to configure a Grafana target
  • Plugin SDK: Resolvables -- build resolvables for your own plugins
  • Release notes -- Docker Compose plugin changelog