Configuration

All configuration options in Polycrate: restrictions on template substitution, workspace.poly structure and keys, block instances, secrets.poly, YAML anchors, container configuration and the global CLI configuration in ~/.polycrate/polycrate.yml.

Configuration

This page documents all configuration options in Polycrate – from workspace settings to block instances.

Important restrictions

What does NOT work

# ❌ WRONG: Jinja2 syntax does NOT work
blocks:
  - name: cert-manager
    from: cargo.ayedo.cloud/ayedo/k8s/cert-manager:0.4.0
    config:
      letsencrypt:
        cloudflare:
          api_token: "{{ secrets.cloudflare_api_token }}"  # ❌ Is NOT substituted!

# ❌ WRONG: Bash variables do NOT work
blocks:
  - name: my-app
    config:
      database_url: "${DATABASE_URL}"      # ❌ Is NOT substituted!
      home_dir: "$HOME/data"               # ❌ Is NOT substituted!

# ❌ WRONG: environment references do NOT work
blocks:
  - name: my-app
    config:
      environment: ${ENV:-production}       # ❌ Is NOT substituted!

What works instead

1. Secrets in a separate file (secrets.poly):

# workspace.poly - non-sensitive configuration
blocks:
  - name: cert-manager
    from: cargo.ayedo.cloud/ayedo/k8s/cert-manager:0.4.0
    config:
      letsencrypt:
        solver:
          type: dns01
          provider: cloudflare
# secrets.poly - sensitive configuration (gets encrypted)
blocks:
  - name: cert-manager
    config:
      letsencrypt:
        cloudflare:
          api_token: "actual-secret-token-here"  # ✅ Real value

2. Configuration is merged at runtime:

Polycrate merges workspace.poly and secrets.poly automatically at runtime. The values from secrets.poly override/extend the values from workspace.poly.

3. Templates in Ansible playbooks:

Jinja2 templating works in Ansible playbooks and templates, NOT in .poly files:

# deploy.yml - Jinja2 works here!
- name: Deploy with config
  kubernetes.core.k8s:
    state: present
    definition:
      apiVersion: v1
      kind: Secret
      metadata:
        name: "{{ block.name }}-secrets"
      data:
        api_token: "{{ block.config.letsencrypt.cloudflare.api_token | b64encode }}"
{# templates/config.yml.j2 - Jinja2 works here! #}
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ block.name }}-config
data:
  environment: {{ block.config.environment }}

Why this restriction?

  1. Security: no unintended code execution in configuration files
  2. Predictability: configuration is deterministic and reviewable
  3. Simplicity: YAML parsing without an additional template engine
  4. Separation of concerns: configuration (.poly) vs. logic (Ansible/playbooks)

→ Secrets management | Ansible integration


Workspace configuration (workspace.poly)

The workspace configuration (default: workspace.poly) contains the settings for a workspace and must be located in the root directory of the workspace.

Custom configuration file

You can specify a custom configuration file for the workspace:

polycrate run my-block install --workspace-config path/to/workspace-config.yml

This can be particularly helpful when using Polycrate in CI/CD pipelines.

Complete workspace.poly structure

workspace.config is not an arbitrary key-value mapping: when the workspace is loaded, it is converted into the typed structure WorkspaceConfig (Polycrate CLI). Only the YAML keys listed below are allowed (in the file without underscores, e.g. blocksroot, artifactsroot). Additional keys under config: are not carried over when reading into this struct (e.g. no free environment: at workspace level – environment/app values belong in blocks[].config or in playbooks).

Under config: there is additionally the optional section release (same indentation as image, blocksroot, …). It is read by polycrate release finalize from the raw workspace.poly and is not part of WorkspaceConfig, but it drives release automation. → Release lifecycle, CLI reference: polycrate release

# workspace.poly (excerpt)
name: my-workspace
organization: my-org
version: 1.0.0

config:
  image:
    reference: cargo.ayedo.cloud/library/polycrate
    version: "0.31.0"
  blocksroot: blocks
  logsroot: .logs
  blocksconfig: block.poly
  workspaceconfig: workspace.poly
  workflowsroot: workflows
  artifactsroot: artifacts
  secretsroot: artifacts/secrets
  containerroot: /workspace
  sshprivatekey: artifacts/secrets/id_rsa
  sshpublickey: artifacts/secrets/id_rsa.pub
  sshkeysource: workspace
  remoteroot: /polycrate
  dockerfile: Dockerfile.poly

  # Optional: global key-value map (advanced)
  globals: {}

  # Optional: pre/post actions for `polycrate release finalize` (not WorkspaceConfig fields)
  release:
    post_actions:
      - block: app
        action: deploy

blocks:
  - name: my-app
    from: cargo.ayedo.cloud/ayedo/k8s/my-app:1.0.0
    config:
      replicas: 3

workflows:
  - name: deploy-all
    steps:
      - name: deploy-app
        block: my-app
        action: install

workspace.config keys (reference)

YAML key Content Short description
image reference, version Container base image for action runs
blocksroot string Directory for block instances (e.g. blocks)
logsroot string Directory for logs (e.g. .logs)
blocksconfig string File name of the block catalogue file (usually block.poly)
workspaceconfig string File name of this workspace file (usually workspace.poly)
workflowsroot string Directory for workflow definitions
artifactsroot string Artifact directory (e.g. artifacts)
secretsroot string Directory for secrets (default artifacts/secrets)
containerroot string Workspace root inside the container (default /workspace)
sshprivatekey / sshpublickey string Paths to SSH keys (context-dependent local vs. container)
sshkeysource string workspace or user
sshusepassphrase bool Passphrase for SSH keys
remoteroot string Default path for remote/special scenarios (default /polycrate)
dockerfile string File name of the custom Dockerfile (usually Dockerfile.poly)
globals map Optional global key-value map

Workspace fields

Field Type Required Description
name string ✅ Unique workspace name
organization string ❌ Organization/team
version string ❌ Workspace version (e.g. for releases)
config map ❌ Workspace-wide configuration (WorkspaceConfig + optional release)
blocks list ❌ Block instances
workflows list ❌ Workflow definitions

Naming rules

Valid names for workspace, organization, blocks, actions and workflows are described centrally:

→ Naming conventions


Block configuration (workspace.poly)

Blocks are instantiated in workspace.poly. Each block instance references a template block via from:.

Block from a registry

blocks:
  - name: kubernetes-cluster
    from: cargo.ayedo.cloud/ayedo/k8s/cluster:1.2.3
    config:
      node_count: 3
      region: eu-central-1

Local template block

blocks:
  - name: my-app
    from: my-app-template  # Block in blocks/my-app-template/
    config:
      port: 8080

Block with kubeconfig reference (legacy)

blocks:
  - name: k8s
    from: cargo.ayedo.cloud/ayedo/k8s/cluster:1.0.0

  - name: my-app
    from: cargo.ayedo.cloud/ayedo/k8s/my-app:1.0.0
    kubeconfig:
      from: k8s  # Legacy: kubeconfig artifact from the k8s block
    config:
      namespace: production

Block instances and inventory

The Ansible inventory lives as inventory.yml in the workspace root and applies to all blocks of this workspace.

blocks:
  - name: infrastructure
    from: cargo.ayedo.cloud/ayedo/infra/servers:1.0.0

  - name: my-service
    from: cargo.ayedo.cloud/ayedo/services/my-service:1.0.0

Block instance fields

Field Type Required Description
name string ✅ Unique instance name in the workspace
from string ❌ Template block (registry or local)
config map ❌ Block-specific configuration
kubeconfig.from string ❌ Legacy: reference to another block instance for the kubeconfig artifact. Best practice: one file artifacts/secrets/kubeconfig.yml without from.
inventory.from string ❌ Legacy: reference to another block instance for the inventory artifact. Best practice: central inventory.yml in the workspace root without from.

Secrets configuration (secrets.poly)

Sensitive configuration belongs in secrets.poly (same format as workspace.poly):

# secrets.poly
blocks:
  - name: my-app
    config:
      database_password: secret
      api_key: abc123

  - name: cert-manager
    config:
      letsencrypt:
        cloudflare:
          api_token: "cloudflare-api-token-here"

After encryption, secrets.poly.age is created:

polycrate workspace encrypt

→ Workspace encryption


YAML anchors for reusable values

Recommended: set identical values literally multiple times

name: acme-production-1
organization: acme

blocks:
  - name: keycloak
    from: cargo.ayedo.cloud/ayedo/k8s/keycloak:0.4.1
    config:
      ingress:
        host: https://id.acme-corp.com
        class: nginx
        tls:
          issuer: letsencrypt-production

  - name: mattermost
    from: cargo.ayedo.cloud/ayedo/k8s/mattermost:0.1.0
    config:
      s3:
        endpoint: https://s3.loopback.ayedo.cloud
      ingress:
        class: nginx

Basic principle of YAML anchors (language reference)

# Define an anchor with &name
my-value: &my-anchor "reusable-value"

# Use the anchor with *name
other-key: *my-anchor  # → "reusable-value"

Example with anchors (only if you use them deliberately)

The following pattern works for pure parsing; after a write operation by Polycrate it may be written back in resolved form:

domain: &domain acme-corp.com
s3_endpoint: &s3_endpoint https://s3.loopback.ayedo.cloud
keycloak_base_url: &keycloak_base_url https://id.acme-corp.com
ingress_class: &ingress_class nginx
letsencrypt_issuer: &letsencrypt_issuer letsencrypt-production

name: acme-production-1
organization: acme

blocks:
  - name: keycloak
    from: cargo.ayedo.cloud/ayedo/k8s/keycloak:0.4.1
    config:
      ingress:
        host: *keycloak_base_url
        class: *ingress_class
        tls:
          issuer: *letsencrypt_issuer

  - name: mattermost
    from: cargo.ayedo.cloud/ayedo/k8s/mattermost:0.1.0
    config:
      s3:
        endpoint: *s3_endpoint
      ingress:
        class: *ingress_class

Common anchor names (reference only)

Anchor Typical value Typical use
&domain example.com Base domain for all services
&s3_endpoint https://s3.provider.com S3-compatible storage
&keycloak_base_url https://id.example.com SSO provider URL
&letsencrypt_issuer letsencrypt-production cert-manager issuer
&ingress_class nginx Ingress controller class

Further restrictions

  1. No sharing with secrets.poly – YAML anchors are only valid within one file; they cannot be used between workspace.poly and secrets.poly.
  2. Manual anchor definition – if you use anchors anyway, define them before the blocks: section so the file stays readable for humans (the structure can still be changed by a Polycrate write operation).

→ Best practices · Production example (historically with anchors)


Container configuration

The Polycrate image can be customized per workspace.

→ The Polycrate container (concept, lifecycle, tooling in the container) · Dockerfile.poly (custom image, config.dockerfile, build with --build)

Pinning the image version

# workspace.poly
config:
  image:
    reference: cargo.ayedo.cloud/library/polycrate
    version: "0.51.3"  # Specific version instead of :latest

Custom Dockerfile

In detail: Dockerfile.poly (path config.dockerfile, FROM convention, build).

For additional tools, create a Dockerfile.poly:

# Dockerfile.poly
FROM cargo.ayedo.cloud/library/polycrate:latest

# Install additional tools (Ubuntu-based!)
RUN apt-get update && apt-get install -y --no-install-recommends \
    postgresql-client \
    redis-tools \
    && rm -rf /var/lib/apt/lists/*
# Build and use the image
polycrate run my-block install --build

Persistent system and community packages (apt)

Everything you write into the image via RUN apt-get install … is permanently available in every Polycrate container of this workspace – e.g. helper programs that your playbooks or roles expect (jq, additional Python modules via python3-pip, database clients):

RUN apt-get update && apt-get install -y --no-install-recommends \
    jq \
    python3-pip \
    && rm -rf /var/lib/apt/lists/*

Ansible Galaxy: installing collections and roles permanently

ansible-galaxy is included in the Polycrate image. Install collections and roles in the build of the custom image so that they do not have to be downloaded again for every action and are available identically everywhere (default paths under /root/.ansible/ or configurable via ansible.cfg in the workspace).

Single collection and role:

RUN ansible-galaxy collection install community.general && \
    ansible-galaxy role install geerlingguy.nginx

Via requirements.yml (recommended for multiple entries):

Place e.g. ansible-galaxy-requirements.yml in the workspace and reference it in the Dockerfile:

# ansible-galaxy-requirements.yml (example)
---
collections:
  - name: community.general
roles:
  - name: geerlingguy.nginx
COPY ansible-galaxy-requirements.yml /tmp/ansible-galaxy-requirements.yml
RUN ansible-galaxy install -r /tmp/ansible-galaxy-requirements.yml

Note: COPY refers to the build context (workspace root during the image build). Paths to roles/collections in playbooks (roles: / collections:) must match the installed names – as with a normal Ansible project.


CLI configuration (~/.polycrate/polycrate.yml)

Global CLI settings are configured in ~/.polycrate/polycrate.yml:

# ~/.polycrate/polycrate.yml
api:
  enabled: true
  url: https://hub.polycrate.io
  api_key: <your-api-key>

hub:
  url: https://hub.polycrate.io
  docs_url: https://docs.ayedo.de

Update and verification checks

After every action or workflow run the CLI optionally performs several checks. All checks are enabled by default and can be disabled globally in polycrate.yml or per invocation via a flag.

Key Flag Default Description
check_updates --check-updates true Checks whether a newer version of the Polycrate CLI is available on the hub.
check_block_updates --check-block-updates true Checks after an action run whether a newer version of the executed block is available on the hub.
check_workspace_behind_remote --check-workspace-behind-remote true Warns before the run if the workspace Git state is behind the remote.
check_maintenance_window --check-maintenance-window true Checks before the run whether the workspace is within the configured maintenance window.
sync_loopback_kubeconfig --sync-loopback-kubeconfig true Historical name: reconcile the workspace-local kubeconfig with the API credential before the run (the implementation matches kind=loopback). General sync: polycrate k8s sync-kubeconfigs.

All checks run non-blocking: errors or an available update do not abort the running command. In CI environments and non-interactive sessions the update checks are skipped automatically.

Global configuration (permanent):

# ~/.polycrate/polycrate.yml
check_updates: true
check_block_updates: true
check_workspace_behind_remote: true
check_maintenance_window: true
sync_loopback_kubeconfig: true

The pre-run reconciliation only applies to a workspace-local kubeconfig (e.g. artifacts/secrets/kubeconfig.yml) — not when falling back to ~/.kube/config. → Kubeconfig synchronization.

Disable per invocation:

# Skip the block update check for this run
polycrate run my-block install --check-block-updates=false

# Skip the workspace stale check and maintenance window
polycrate run my-block install \
  --check-workspace-behind-remote=false \
  --check-maintenance-window=false

# Skip the pre-run kubeconfig reconciliation
polycrate run my-block install --sync-loopback-kubeconfig=false

Runtime directory (CLI flag)

The global flag --runtime-dir is not part of polycrate.yml; it is set on the command line. It determines where Polycrate stores temporary files per transaction (default: ~/.polycrate/run). Together with --config-dir you can, in restricted environments (sandboxes, CI, remote editors), point both the configuration base and the runtime storage at writable paths without extending HOME.

→ In detail: CLI reference — runtime directory

SSH configuration (with sshkeysource: user)

If the workspace uses config.sshkeysource: user, the path to the personal SSH key can be set here:

ssh:
  user_key_path: "~/.ssh/work_ed25519"   # optional; default: ~/.ssh/id_rsa
  user_key_path_by_workspace:            # optional; workspace.name → key path
    laser24: "~/.ssh/laser_ed25519"
    archenemy: "~/.ssh/ayedo_work"

→ SSH integration

→ API integration


Recommendations (short)

  • Explicit block tags (:1.2.3), full registry paths
  • Secrets only in secrets.poly; no templates/env substitution in .poly
  • Set recurring values literally (YAML anchors see above — not recommended)
  • Maintain CHANGELOG.poly for workspace and blocks

→ Best practices


See also