DEV Community

Cover image for Production-Ready Docker Compose Collection with kikplate
Moeid Heidari
Moeid Heidari

Posted on

Production-Ready Docker Compose Collection with kikplate

Docker Compose Plates: Reusable Infrastructure Stacks with kikplate

Infrastructure setup is one of the most repeated tasks in software development. Every new project needs a database, a cache, a message broker, or an observability stack. Teams copy configurations from old projects, adjust port numbers, forget to add health checks, use unofficial images, and eventually end up with inconsistent setups across their environments.

This article walks through a curated collection of twenty Docker Compose plates built for the kikplate template generation system. Each plate is a parameterised, versioned, composable unit that generates a production-ready stack from a single command.

Repository
Kikplate.dev


What is kikplate?

kikplate is a developer toolchain for generating project files from versioned, schema-driven templates.

A plate lives on its own Git branch. It defines:

  • A typed schema of input variables
  • Optional feature modules
  • A set of template files

Templates are fetched from GitHub, rendered with the provided values, and written to a local output directory.

Install on macOS and Linux

brew tap kikplate/homebrew-kikplate
brew install --cask kik
Enter fullscreen mode Exit fullscreen mode

Install on Windows

scoop bucket add kikplate https://github.com/kikplate/scoop-bucket.git
scoop install kik
Enter fullscreen mode Exit fullscreen mode

The Plate Format

Every plate in the collection follows the same directory structure:

plate.yaml          schema, modules, and file definitions
values.yaml         default values for all schema variables
templates/
    docker-compose.yml.tmpl     main compose template
    .env.tmpl                   secrets template
    README.md.tmpl              usage documentation
    ...                         additional config files where needed
Enter fullscreen mode Exit fullscreen mode

The plate.yaml file acts as the manifest.

Example: the PostgreSQL plate.

name: postgres
owner: kikplate-public
description: PostgreSQL 16 database with optional pgAdmin 4 web interface,
  persistent named volumes, health checks, and bridge network isolation.
version: 1.0.0
category: database

tags:
  - postgres
  - postgresql
  - database
  - pgadmin
  - docker-compose

schema:
  projectName:
    type: string
    default: postgres

  postgresVersion:
    type: string
    default: "16"

  postgresUser:
    type: string
    default: app

  postgresDb:
    type: string
    default: app

  postgresPort:
    type: int
    default: 5432

  timezone:
    type: string
    default: UTC

  sharedBuffers:
    type: string
    default: "128MB"

  maxConnections:
    type: int
    default: 100

  pgadminVersion:
    type: string
    default: "8"

  pgadminEmail:
    type: string
    default: admin@example.com

  pgadminPort:
    type: int
    default: 5050

modules:
  pgadmin:
    enabled: true

files:
  - path: docker-compose.yml
    template: https://raw.githubusercontent.com/kikplate/docker-compose-plates/postgres/templates/docker-compose.yml.tmpl

  - path: .env
    template: https://raw.githubusercontent.com/kikplate/docker-compose-plates/postgres/templates/.env.tmpl

  - path: README.md
    template: https://raw.githubusercontent.com/kikplate/docker-compose-plates/postgres/templates/README.md.tmpl
Enter fullscreen mode Exit fullscreen mode

Schema

The schema section defines every configurable value, including:

  • Type
  • Default value
  • Validation rules (when applicable)

Modules

The modules section defines optional capabilities that can be enabled or disabled during generation.

Files

The files section specifies every generated output file and the remote template that should be rendered.


values.yaml

The values.yaml file is a flat copy of all schema defaults and serves as the primary customization point.

projectName: "postgres"
postgresVersion: "16"
postgresUser: "app"
postgresDb: "app"
postgresPort: 5432
timezone: "UTC"
sharedBuffers: "128MB"
maxConnections: 100
pgadminVersion: "8"
pgadminEmail: "admin@example.com"
pgadminPort: 5050

modules:
  pgadmin:
    enabled: true
Enter fullscreen mode Exit fullscreen mode

What Gets Generated

Running:

kik generate kikplate-public/postgres
Enter fullscreen mode Exit fullscreen mode

produces three files.


docker-compose.yml

The Compose file is rendered from the template. Image versions, container names, ports, command flags, network names, and optional services are generated from the provided schema values.

services:
  postgres:
    image: postgres:16-alpine
    container_name: myapp-postgres
    restart: unless-stopped
    stop_grace_period: 30s
    security_opt:
      - no-new-privileges:true
    env_file:
      - .env
    environment:
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: ${POSTGRES_DB}
      PGTZ: UTC
    command: >
      postgres
        -c shared_buffers=128MB
        -c max_connections=100
        -c timezone=UTC
        -c log_timezone=UTC
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 10s
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"
    networks:
      - db_net

  pgadmin:
    image: dpage/pgadmin4:8
    container_name: myapp-pgadmin
    restart: unless-stopped
    stop_grace_period: 30s
    security_opt:
      - no-new-privileges:true
    env_file:
      - .env
    environment:
      PGADMIN_DEFAULT_EMAIL: ${PGADMIN_EMAIL}
      PGADMIN_DEFAULT_PASSWORD: ${PGADMIN_PASSWORD}
      PGADMIN_CONFIG_ENHANCED_COOKIE_PROTECTION: "True"
      PGADMIN_LISTEN_PORT: 80
    ports:
      - "5050:80"
    volumes:
      - pgadmin_data:/var/lib/pgadmin
    depends_on:
      postgres:
        condition: service_healthy
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"
    networks:
      - db_net

volumes:
  postgres_data:
  pgadmin_data:

networks:
  db_net:
    driver: bridge
Enter fullscreen mode Exit fullscreen mode

.env

Secrets and sensitive values are generated into a separate environment file.

Placeholder values make it obvious what should be changed before a deployment.

POSTGRES_USER=app
POSTGRES_PASSWORD=changeme
POSTGRES_DB=app

PGADMIN_EMAIL=admin@example.com
PGADMIN_PASSWORD=changeme
Enter fullscreen mode Exit fullscreen mode

README.md

A project-specific README is generated alongside the stack.

The rendered documentation includes:

  • Startup instructions
  • Service URLs
  • Port mappings
  • Connection strings
  • Operational guidance

All values are generated using the parameters provided during plate generation.


Why Use Plates?

Instead of copying Compose files between projects, teams can generate consistent infrastructure stacks from a controlled source.

Benefits include:

  • Versioned infrastructure definitions
  • Standardised health checks
  • Consistent security settings
  • Reusable configuration patterns
  • Optional feature modules
  • Built-in documentation generation
  • Reduced setup time for new projects
  • Easier maintenance across teams

Every generated stack follows the same design principles and can be reproduced at any time from its original plate version.


The Collection

The repository currently contains twenty Docker Compose plates organised into five categories.

Each plate defines:

  • A service stack
  • Schema-driven configuration
  • Optional modules
  • Generated documentation
  • Environment templates

Each row in the collection documents:

  • The plate slug
  • The generated Docker Compose services
  • Available optional modules

This allows teams to quickly select the required infrastructure stack and generate a production-ready environment with a single command.

Database

Database category showing PostgreSQL, MySQL, MariaDB, MongoDB and Redis Docker Compose plates and their optional modules

Backend

Backend category showing application platforms and workflow services with their supported dependencies and optional modules

DevOps

DevOps category showing monitoring, observability, message broker and infrastructure related Docker Compose plates

Security

Security category showing identity management, secret management and security-focused Docker Compose plates

Other

Additional utility and supporting Docker Compose plates available in the collection

Architecture and Composition

The diagram below shows all twenty plates and the composition relationships between them. Solid arrows indicate a default-enabled composition. Dashed arrows indicate an optional module.

Architecture diagram showing all Docker Compose plates and their composition relationships, including optional and required dependencies between services

How Composition Works

Many infrastructure stacks depend on other services. For example, Keycloak typically requires a PostgreSQL database.

Instead of duplicating database definitions, kikplate supports plate composition, allowing one plate to reuse templates from another plate.

The Keycloak plate composes the PostgreSQL plate by referencing its templates directly in plate.yaml.

modules:
  postgres:
    enabled: true

  pgadmin:
    enabled: false

files:
  - path: docker-compose.yml
    template: https://raw.githubusercontent.com/kikplate/docker-compose-plates/keycloak/templates/docker-compose.yml.tmpl

  - path: docker-compose.postgres.yml
    template: https://raw.githubusercontent.com/kikplate/docker-compose-plates/postgres/templates/docker-compose.yml.tmpl
    condition: modules.postgres.enabled

  - path: .env
    template: https://raw.githubusercontent.com/kikplate/docker-compose-plates/keycloak/templates/.env.tmpl

  - path: README.md
    template: https://raw.githubusercontent.com/kikplate/docker-compose-plates/keycloak/templates/README.md.tmpl
Enter fullscreen mode Exit fullscreen mode

When modules.postgres.enabled is set to true, kikplate:

  1. Fetches the PostgreSQL template from the PostgreSQL plate branch.
  2. Renders it using the values provided to the Keycloak plate.
  3. Generates a dedicated docker-compose.postgres.yml file.
  4. Includes that generated Compose file in the final deployment.

This approach enables reusable infrastructure building blocks without copying configuration between plates.


Shared Schema

To ensure the PostgreSQL template always receives valid input values, the Keycloak plate includes all PostgreSQL-related variables in its own schema.

schema:
  projectName:
    type: string
    default: keycloak

  keycloakVersion:
    type: string
    default: "24.0"

  postgresVersion:
    type: string
    default: "16"

  postgresPort:
    type: int
    default: 5432

  timezone:
    type: string
    default: UTC

  sharedBuffers:
    type: string
    default: "128MB"

  maxConnections:
    type: int
    default: 100

  pgadminVersion:
    type: string
    default: "8"

  pgadminPort:
    type: int
    default: 5050

  pgadminEmail:
    type: string
    default: admin@example.com

  keycloakPort:
    type: int
    default: 8080

  keycloakLogLevel:
    type: string
    default: INFO
Enter fullscreen mode Exit fullscreen mode

Because all required PostgreSQL variables are available in the schema, the PostgreSQL templates can be rendered independently without requiring any additional configuration.


Docker Compose Includes

The Keycloak Compose template uses Docker Compose's include capability to compose the generated PostgreSQL stack into the final deployment.

{{ if .modules.postgres.enabled }}
include:
  - docker-compose.postgres.yml
{{ end }}

services:
  keycloak:
    image: quay.io/keycloak/keycloak:{{ .keycloakVersion }}
    container_name: {{ .projectName }}-keycloak
    restart: unless-stopped

    environment:
      KC_DB: postgres
      KC_DB_URL: jdbc:postgresql://postgres:5432/keycloak
      KC_DB_USERNAME: ${POSTGRES_USER}
      KC_DB_PASSWORD: ${POSTGRES_PASSWORD}
      KEYCLOAK_ADMIN: ${KEYCLOAK_ADMIN}
      KEYCLOAK_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD}

    ports:
      - "{{ .keycloakPort }}:8080"

    depends_on:
      postgres:
        condition: service_healthy

    networks:
      - kc_net
      - db_net

networks:
  kc_net:
    driver: bridge
Enter fullscreen mode Exit fullscreen mode

When PostgreSQL is enabled:

  • docker-compose.postgres.yml is generated.
  • The Keycloak stack includes it automatically.
  • The PostgreSQL service exposes the db_net network.
  • The Keycloak container joins db_net.
  • Keycloak can connect to PostgreSQL using the hostname postgres.
  • Startup order is controlled through health checks and depends_on.

This creates a loosely coupled but fully integrated deployment.


Generated Output

Generating the Keycloak plate produces the following files:

generated/
├── docker-compose.yml
├── docker-compose.postgres.yml
├── .env
└── README.md
Enter fullscreen mode Exit fullscreen mode

docker-compose.yml

Contains:

  • The Keycloak service
  • Compose include directives
  • Keycloak-specific networking and configuration

docker-compose.postgres.yml

Contains:

  • PostgreSQL service
  • Persistent volumes
  • Health checks
  • Logging configuration
  • db_net network

.env

Contains shared credentials used by both services.

POSTGRES_USER=app
POSTGRES_PASSWORD=changeme
POSTGRES_DB=keycloak

KEYCLOAK_ADMIN=admin
KEYCLOAK_ADMIN_PASSWORD=changeme
Enter fullscreen mode Exit fullscreen mode

README.md

Contains deployment-specific documentation including:

  • Startup instructions
  • Access URLs
  • Port mappings
  • Login credentials guidance

Benefits of Composition

Plate composition provides several advantages:

  • Reuse existing infrastructure definitions
  • Eliminate configuration duplication
  • Keep service definitions independent
  • Share common variables across related stacks
  • Generate modular Docker Compose deployments
  • Simplify maintenance and versioning
  • Allow optional dependencies through modules

Instead of embedding PostgreSQL directly into the Keycloak plate, the Keycloak plate composes the PostgreSQL plate as a reusable building block. This keeps both plates focused on a single responsibility while still allowing them to work together as a complete deployment.

Composition Examples

The same composition pattern is used throughout the collection.

Examples include:

  • WordPress → composes MySQL, optionally Redis.
  • n8n → composes PostgreSQL, optionally Redis.
  • Grafana and observability stacks → optionally compose PostgreSQL as the backend database.

Each plate remains focused on a single responsibility while reusing infrastructure components from other plates where appropriate.


Generating a Plate

The simplest generation flow uses interactive prompts and accepts default values.

kik generate kikplate-public/postgres
Enter fullscreen mode Exit fullscreen mode

Generate from a Values File

To avoid prompts entirely, provide a values file.

kik generate kikplate-public/postgres -f values.yaml
Enter fullscreen mode Exit fullscreen mode

Every plate includes a values.yaml file containing all schema variables with default values.

Teams typically:

  1. Generate a values file.
  2. Customise it for their environment.
  3. Commit it into source control.
  4. Regenerate infrastructure at any time from the committed values.

This makes infrastructure reproducible and eliminates manual configuration.


Override Individual Values

Specific values can be overridden directly on the command line.

kik generate kikplate-public/postgres \
  --set projectName=myapp \
  --set postgresPort=5433
Enter fullscreen mode Exit fullscreen mode

Generate into a Specific Directory

Generated files can be written to a dedicated location.

kik generate kikplate-public/postgres \
  --output-dir ./infra/postgres
Enter fullscreen mode Exit fullscreen mode

Generate Directly to a Repository

Generated output can also be pushed directly into a remote repository.

kik generate kikplate-public/postgres \
  -f values.yaml \
  --repo https://github.com/yourorg/infra.git
Enter fullscreen mode Exit fullscreen mode

Consistency Across Every Plate

Regardless of the service being provisioned, every plate follows the same operational and security standards.

Secrets Management

No passwords or secrets are embedded in templates.

Generated secrets are written to a separate .env file with placeholder values.

POSTGRES_PASSWORD=changeme
REDIS_PASSWORD=changeme
Enter fullscreen mode Exit fullscreen mode

Developers replace these values before starting the stack.


Health Checks

Every stateful service defines a health check.

Dependent services wait until dependencies become healthy before starting.

depends_on:
  postgres:
    condition: service_healthy
Enter fullscreen mode Exit fullscreen mode

This guarantees a reliable startup sequence during:

docker compose up
Enter fullscreen mode Exit fullscreen mode

Logging

Every service uses the Docker json-file logging driver.

logging:
  driver: json-file
  options:
    max-size: "10m"
    max-file: "3"
Enter fullscreen mode Exit fullscreen mode

Benefits include:

  • Log rotation
  • Predictable disk usage
  • Consistent behaviour across stacks

Network Isolation

Each plate defines its own named Docker bridge network.

networks:
  app_net:
    driver: bridge
Enter fullscreen mode Exit fullscreen mode

Services communicate only within their assigned network.

When plates are composed together, the dependent service joins the required networks to enable controlled communication between components.


Security Options

All containers run with:

security_opt:
  - no-new-privileges:true
Enter fullscreen mode Exit fullscreen mode

Additional Linux capabilities are not granted unless the application explicitly requires them.

For example:

cap_add:
  - IPC_LOCK
Enter fullscreen mode Exit fullscreen mode

is included for Vault because its functionality depends on memory locking.


Graceful Shutdown

Every container defines a shutdown grace period.

stop_grace_period: 30s
Enter fullscreen mode Exit fullscreen mode

This allows databases, queues, and brokers to:

  • Complete pending writes
  • Flush data safely
  • Exit cleanly

before container termination.


Official Images

Every plate uses official vendor-maintained container images.

Examples include:

  • postgres
  • mysql
  • redis
  • grafana
  • prometheus
  • quay.io/keycloak/keycloak

Images are version-pinned through schema defaults and can be overridden at generation time.

No community-maintained wrappers or floating latest tags are used.


How Teams Use This

Small Development Teams

A backend team starts a new service and requires:

  • PostgreSQL
  • Redis

Without automation, the team typically:

  1. Copies an old docker-compose.yml.
  2. Adjusts service names.
  3. Changes ports.
  4. Updates image versions.
  5. Fixes broken health checks.
  6. Troubleshoots startup issues.

With kikplate:

kik generate kikplate-public/postgres \
  -f postgres-values.yaml \
  --output-dir ./infra/postgres

kik generate kikplate-public/redis \
  -f redis-values.yaml \
  --output-dir ./infra/redis
Enter fullscreen mode Exit fullscreen mode

Two commands produce two standardised infrastructure stacks.

The generated configurations are:

  • Current
  • Version-controlled
  • Health-checked
  • Secure by default
  • Reproducible

Platform Teams

Platform engineering teams often need to standardise infrastructure across dozens of services.

A common approach is to maintain approved values files.

When a new service is created, developers:

  1. Select an approved plate.
  2. Generate infrastructure using the approved values file.
  3. Commit the generated output alongside their application.

This ensures every service begins with infrastructure aligned to organisational standards.


Updating Existing Deployments

When a plate receives updates such as:

  • New image versions
  • Security improvements
  • Additional configuration options
  • Operational enhancements

teams simply regenerate the files.

kik generate kikplate-public/postgres -f values.yaml
Enter fullscreen mode Exit fullscreen mode

The generated changes can be reviewed using normal source control workflows.

The plate remains the single source of truth for infrastructure configuration.


Repository Structure

The Docker Compose Plates collection is hosted at:

Docker Compose Plates Repository

The repository uses a branch-per-plate model.

Individual Plate Branches

Each plate resides on its own branch.

Examples:

postgres
redis
mysql
keycloak
wordpress
n8n
grafana
Enter fullscreen mode Exit fullscreen mode

Each branch contains:

plate.yaml
values.yaml
templates/
Enter fullscreen mode Exit fullscreen mode

plate.yaml

Defines:

  • Schema
  • Modules
  • Template files
  • Metadata

values.yaml

Contains default values for all schema variables.

templates/

Contains the source templates that are rendered during generation.

Examples:

templates/
├── docker-compose.yml.tmpl
├── .env.tmpl
├── README.md.tmpl
└── additional configuration templates
Enter fullscreen mode Exit fullscreen mode

Main Branch

The main branch contains:

  • Documentation
  • Guides
  • The plate catalogue
  • Repository metadata

It acts as the public entry point for the collection.


All Branch

The all branch contains every plate as a subdirectory.

all/
├── postgres/
├── redis/
├── keycloak/
├── wordpress/
├── grafana/
└── ...
Enter fullscreen mode Exit fullscreen mode

This branch exists primarily for:

  • Cross-plate development
  • Local testing
  • Refactoring shared functionality
  • Reviewing the complete collection in a single working tree

Summary

Docker Compose Plates turns infrastructure configuration from a copy-and-paste exercise into a repeatable generation process.

By combining:

  • Typed schemas
  • Versioned templates
  • Optional modules
  • Plate composition
  • Standardised operational practices

teams can generate production-ready infrastructure stacks in seconds while maintaining consistency across every environment and every project.

Top comments (0)