Skip to content

Deployment Guide

This guide covers installing Mantle, running it in production, and hardening your deployment. For trigger configuration and the REST API, see the Server Guide. For authentication setup, see the Authentication & RBAC Guide.

Prerequisites

Before deploying Mantle, you need:

  • Postgres 14+ — Mantle’s single point of state. All workflow definitions, executions, credentials, and audit events live in the database.
  • Encryption key — a 32-byte hex-encoded key for encrypting credentials at rest. See the Secrets Guide for details.
  • Domain and TLS — required for production. Mantle can terminate TLS directly or run behind a reverse proxy.

Installation Methods

Binary Download

Download the latest release from GitHub Releases:

# Linux (amd64)
curl -Lo mantle https://github.com/dvflw/mantle/releases/latest/download/mantle-linux-amd64
chmod +x mantle
sudo mv mantle /usr/local/bin/

# macOS (Apple Silicon)
curl -Lo mantle https://github.com/dvflw/mantle/releases/latest/download/mantle-darwin-arm64
chmod +x mantle
sudo mv mantle /usr/local/bin/

Verify the installation:

mantle version
# mantle v0.1.0 (791fa83, built 2026-03-18T00:00:00Z)

Go Install

If you have Go 1.25+ installed:

go install github.com/dvflw/mantle/cmd/mantle@latest

The binary is placed in $GOPATH/bin (or $HOME/go/bin by default).

Docker

Pull the official image:

docker pull ghcr.io/dvflw/mantle:0.1.0

Run with environment variables:

docker run -d \
  -p 8080:8080 \
  -e MANTLE_DATABASE_URL="postgres://mantle:secret@host.docker.internal:5432/mantle?sslmode=disable" \
  -e MANTLE_ENCRYPTION_KEY="your-64-char-hex-key" \
  ghcr.io/dvflw/mantle:0.1.0 serve

Helm Chart

For Kubernetes deployments, use the included Helm chart. See Production Deployment (Kubernetes/Helm) below.

Quick Start (Docker Compose)

The fastest way to get Mantle running locally with Postgres:

git clone https://github.com/dvflw/mantle.git && cd mantle
docker compose up -d

This starts Postgres 16 on localhost:5432 with user mantle, password mantle, and database mantle.

Run migrations and start the server:

export MANTLE_DATABASE_URL="postgres://mantle:mantle@localhost:5432/mantle?sslmode=disable"
export MANTLE_ENCRYPTION_KEY=$(openssl rand -hex 32)

mantle init
mantle serve
Running migrations...
Migrations complete.
Starting server on :8080
Cron scheduler started (poll interval: 30s)

Mantle is now running at http://localhost:8080. See the Getting Started guide for your first workflow.

Production Deployment (Kubernetes/Helm)

The recommended way to run Mantle in production is with the included Helm chart.

Basic Install

helm install mantle charts/mantle \
  --set database.url="postgres://mantle:secret@db.internal:5432/mantle?sslmode=require" \
  --set encryption.key="your-64-char-hex-key" \
  --set replicaCount=3

Values Reference

ValueDefaultDescription
image.repositoryghcr.io/dvflw/mantleContainer image repository
image.tagChart appVersionContainer image tag
image.pullPolicyIfNotPresentImage pull policy
replicaCount1Number of server replicas
database.urlPostgres connection string (required)
encryption.key32-byte hex encryption key (required)
resources.requests.cpu100mCPU request
resources.requests.memory128MiMemory request
resources.limits.cpu500mCPU limit
resources.limits.memory512MiMemory limit
probes.liveness.path/healthzLiveness probe path
probes.readiness.path/readyzReadiness probe path
pdb.enabledfalseEnable PodDisruptionBudget
pdb.minAvailable1Minimum available pods during disruption
securityContext.runAsNonRoottrueRun as non-root user
securityContext.readOnlyRootFilesystemtrueRead-only root filesystem
ingress.enabledfalseEnable Ingress resource
ingress.className""Ingress class name
ingress.hosts[]Ingress host rules
ingress.tls[]Ingress TLS configuration

TLS Termination

Option 1: Ingress (recommended for Kubernetes). Configure TLS at the Ingress level:

# values.yaml
ingress:
  enabled: true
  className: nginx
  hosts:
    - host: mantle.company.com
      paths:
        - path: /
          pathType: Prefix
  tls:
    - secretName: mantle-tls
      hosts:
        - mantle.company.com

Option 2: Native TLS. Mantle can terminate TLS directly:

mantle serve --tls-cert /etc/mantle/tls.crt --tls-key /etc/mantle/tls.key

Or via environment variables:

export MANTLE_TLS_CERT="/etc/mantle/tls.crt"
export MANTLE_TLS_KEY="/etc/mantle/tls.key"

Database Migrations

The Helm chart includes a pre-install/pre-upgrade hook Job that runs mantle init before the new version starts serving traffic. You do not need to run migrations separately. The migration job uses database-level locking, so it is safe with multiple replicas.

Configuration Checklist

Use this checklist when deploying to production. Every item maps to an environment variable or mantle.yaml field.

SettingEnv VarRequiredNotes
Database URLMANTLE_DATABASE_URLYesUse sslmode=require in production
Encryption keyMANTLE_ENCRYPTION_KEYYes32 bytes, hex-encoded (64 characters)
API listen addressMANTLE_API_ADDRESSNoDefault :8080
TLS certificateMANTLE_TLS_CERTNoPath to PEM-encoded certificate
TLS private keyMANTLE_TLS_KEYNoPath to PEM-encoded private key
OIDC issuer URLMANTLE_AUTH_OIDC_ISSUER_URLNoRequired if using SSO
OIDC client IDMANTLE_AUTH_OIDC_CLIENT_IDNoRequired if using SSO
OIDC audienceMANTLE_AUTH_OIDC_AUDIENCENoRequired if using SSO
OIDC allowed domainsMANTLE_AUTH_OIDC_ALLOWED_DOMAINSNoComma-separated domain list
AWS regionAWS_REGIONNoRequired if using AWS Secrets Manager
GCP projectMANTLE_GCP_PROJECTNoRequired if using GCP Secret Manager
Azure vault URLMANTLE_AZURE_VAULT_URLNoRequired if using Azure Key Vault
Execution retentionMANTLE_RETENTION_EXECUTION_DAYSNoDefault 90. Days to keep completed executions
Audit retentionMANTLE_RETENTION_AUDIT_DAYSNoDefault 365. Days to keep audit events
Allowed AI base URLsMANTLE_AI_ALLOWED_BASE_URLSNoComma-separated. Restricts which AI endpoints connectors can call
Log levelMANTLE_LOG_LEVELNoDefault info. Options: debug, info, warn, error

Production Hardening

Enable TLS

Never run Mantle over plain HTTP in production. Use one of:

  • Ingress with TLS termination (see TLS Termination above)
  • Native TLS with --tls-cert and --tls-key
  • A reverse proxy (nginx, Caddy, HAProxy) that terminates TLS

Set Resource Limits

Always set CPU and memory limits to prevent a single workflow execution from consuming all available resources:

# values.yaml
resources:
  requests:
    cpu: 250m
    memory: 256Mi
  limits:
    cpu: "1"
    memory: 1Gi

Configure PodDisruptionBudget

For high availability, enable the PDB to ensure at least one replica stays available during node maintenance:

# values.yaml
replicaCount: 3
pdb:
  enabled: true
  minAvailable: 1

Set Retention Policies

Configure retention to prevent unbounded storage growth:

# mantle.yaml
retention:
  execution_days: 90
  audit_days: 365

Or via environment variables:

export MANTLE_RETENTION_EXECUTION_DAYS=90
export MANTLE_RETENTION_AUDIT_DAYS=365

Mantle runs a background cleanup job that deletes completed executions and audit events older than the configured retention period.

Restrict AI Base URLs

If your workflows use the AI connector, restrict which endpoints can be called to prevent exfiltration:

# mantle.yaml
ai:
  allowed_base_urls:
    - "https://api.openai.com"
    - "https://api.anthropic.com"

Requests to any other base URL are rejected at execution time.

Monitor with Prometheus

Scrape the /metrics endpoint with Prometheus or a compatible collector:

# prometheus.yml
scrape_configs:
  - job_name: mantle
    static_configs:
      - targets: ["mantle:8080"]

See the Observability Guide for metric names, example PromQL queries, and Grafana dashboard configuration.

Set Up Database Backups

Postgres is Mantle’s single point of state. Set up automated backups:

  • Managed Postgres (RDS, Cloud SQL, Azure Database): Enable automated snapshots and WAL archiving
  • Self-hosted Postgres: Schedule pg_dump and configure WAL archiving

See the Server Guide for detailed backup procedures, recovery steps, and RPO/RTO guidance.

Security Context

The Helm chart defaults to a secure pod configuration:

securityContext:
  runAsNonRoot: true
  readOnlyRootFilesystem: true
  allowPrivilegeEscalation: false
  capabilities:
    drop:
      - ALL

Do not override these defaults unless you have a specific requirement.

Further Reading