Registry

Polycrate blocks are pushed to and pulled from OCI-compatible registries like Docker images, by default cargo.ayedo.cloud. Describes the architecture, authentication via Docker credential helper, configuration and exclude patterns on push.

Registry

What is the Polycrate registry?

Polycrate blocks can be pushed to and pulled from OCI-compatible registries, similar to Docker images. This enables the distribution and reuse of blocks across teams and organizations.

As the default registry, Polycrate uses cargo.ayedo.cloud to pull or push blocks.

Registry architecture

flowchart TB
    Dev("Developer") --> Push["polycrate blocks push"]
    Push --> Package["Block is packaged"]
    Package --> OCI(("OCI Artifact"))
    OCI --> Registry(("OCI Registry"))

    Registry --> Pull["polycrate blocks pull"]
    Pull --> Extract["Extract block"]
    Extract --> Workspace["Workspace blocks/"]

    User("User") --> AutoPull["polycrate run --blocks-auto-pull"]
    AutoPull --> Check{Block local?}
    Check -->|No| Pull
    Check -->|Yes| Run["Run action"]

Authentication

Polycrate implements no authentication of its own with the registry, but instead uses your local Docker credential helper.

To authenticate with a registry:

# Log in to the registry
docker login cargo.ayedo.cloud

# Or to a custom registry
docker login my-registry.com

# Credentials are stored in ~/.docker/config.json
# Polycrate uses them automatically

Registry configuration

Registry URL in the block name

The registry URL must always be specified in the block name – both for pull and for push:

# Full registry path (REQUIRED)
polycrate blocks pull registry.my-org.com/team/my-block:1.0.0

# From cargo.ayedo.cloud
polycrate blocks pull cargo.ayedo.cloud/ayedo/k8s/cloudnative-pg:1.3.1

Registry proxying for air-gapped environments

For air-gapped/on-premise environments where external registries are not reachable, you can configure registry proxies at the workspace level. This makes it possible to transparently redirect block references from one registry to another.

Use case:

In an air-gapped environment without access to cargo.ayedo.cloud you can use a local OCI registry such as Harbor as a proxy that has mirrored the blocks.

Configuration in workspace.poly:

name: my-workspace

registry:
  endpoint: registry.enterprise.com
  # Move credentials to secrets.poly (see below)
  proxy:
    # Pattern-based registry rewrites
    - from: cargo.ayedo.cloud/ayedo
      to: registry.enterprise.com/cargo-proxy/ayedo

    - from: hub.polycrate.io
      to: registry.enterprise.com/polyhub-mirror

    - from: ghcr.io/company
      to: registry.enterprise.com/github-mirror/company

blocks:
  # This reference is rewritten automatically
  - name: k8s-nginx
    from: cargo.ayedo.cloud/ayedo/k8s/haproxy:2.0.4
    # → becomes: registry.enterprise.com/cargo-proxy/ayedo/k8s/nginx:1.0.0

Credentials in secrets.poly:

# secrets.poly (encrypted by workspace encrypt)
registry:
  username: admin
  password: geheim123
  proxy:
    - from: cargo.ayedo.cloud/ayedo
      username: proxy-user
      password: proxy-geheim

How it works:

  1. Polycrate checks every block registry URL against all proxy entries
  2. On a match, from is replaced by to in the URL
  3. Optionally, per-proxy credentials can be specified
  4. If there is no match, the original URL is used

Example rewrite logic:

Original:  cargo.ayedo.cloud/ayedo/k8s/haproxy:2.0.4
From:      cargo.ayedo.cloud/ayedo
To:        registry.enterprise.com/cargo-proxy/ayedo
Result:    registry.enterprise.com/cargo-proxy/ayedo/k8s/nginx:1.0.0

Multiple proxy patterns:

registry:
  proxy:
    # Specific namespace proxies (checked first)
    - from: cargo.ayedo.cloud/ayedo/k8s
      to: registry.enterprise.com/kubernetes-blocks

    # Catch-all for remaining ayedo blocks
    - from: cargo.ayedo.cloud/ayedo
      to: registry.enterprise.com/ayedo-mirror

    # Other registries
    - from: docker.io/library
      to: registry.enterprise.com/dockerhub-library

Best practices for registry proxying:

  1. Specific patterns first: sort proxies from specific to generic
  2. Credentials management: use secrets.poly for passwords or docker login
  3. Registry mirroring: make sure the proxy registry contains the blocks
  4. Testing: test the proxy configuration with --loglevel 2 for debug output

Setting up Harbor as a registry proxy:

Harbor is a popular OCI registry for on-premise deployments:

# workspace.poly - Harbor proxy setup
registry:
  endpoint: harbor.company.com
  proxy:
    - from: cargo.ayedo.cloud
      to: harbor.company.com/proxy-cache
# secrets.poly - credentials (store encrypted!)
registry:
  username: admin
  password: harbor-admin-password
  proxy:
    - from: cargo.ayedo.cloud
      username: proxy-bot
      password: proxy-bot-password

In Harbor: 1. Create a project proxy-cache 2. Configure a proxy cache endpoint to cargo.ayedo.cloud 3. Harbor loads blocks on demand and caches them locally

Troubleshooting:

# Enable debug mode for registry resolution
polycrate blocks pull cargo.ayedo.cloud/ayedo/k8s/haproxy:2.0.4 --loglevel 2

# Output shows:
# INFO: Registry proxy match found: cargo.ayedo.cloud/ayedo/k8s/haproxy -> registry.enterprise.com/cargo-proxy/ayedo/k8s/nginx
# DEBUG: Using proxy credentials for registry: registry.enterprise.com/cargo-proxy

Pulling blocks

Manual pull

To add blocks dynamically from the registry to the workspace:

# Pull a block with a specific version
polycrate blocks pull registry.my-org.com/infra/postgres:1.2.0

# Pull from cargo.ayedo.cloud
polycrate blocks pull cargo.ayedo.cloud/ayedo/k8s/cloudnative-pg:1.3.1

# Pull from Docker Hub (if blocks are hosted there)
polycrate blocks pull index.docker.io/myuser/my-block:1.0.0

The block is downloaded to blocks/<block-name>/ in the workspace.

Automatic pull with --blocks-auto-pull

Instead of pulling blocks manually, you can use the --blocks-auto-pull flag:

# When running an action
polycrate run my-app install --blocks-auto-pull

# When running a workflow
polycrate workflows run deploy-stack --blocks-auto-pull

# When inspecting a block
polycrate blocks inspect my-app --blocks-auto-pull

Polycrate then automatically pulls all missing dependencies.

Pull process

sequenceDiagram
    participant CLI
    participant Docker
    participant Registry
    participant Workspace

    CLI->>CLI: Parse block name & version
    CLI->>Docker: Check credentials
    Docker-->>CLI: Credentials OK
    CLI->>Registry: Request manifest
    Registry-->>CLI: Manifest & Layers
    CLI->>Registry: Download layers
    Registry-->>CLI: Layer data
    CLI->>CLI: Extract package
    CLI->>Workspace: Write blocks/<name>/
    CLI-->>CLI: Pull complete

Uninstalling blocks

# Remove a block from the workspace
polycrate blocks uninstall postgres-base

# Or delete manually
rm -rf blocks/postgres-base

Pushing blocks

Basic push

To push a block to an OCI-compatible registry:

# Full registry path (REQUIRED)
polycrate blocks push registry.my-org.com/team/my-block

# Push to cargo.ayedo.cloud
polycrate blocks push cargo.ayedo.cloud/my-org/k8s/my-block

Example:

# blocks/my-block/block.poly
name: my-block
version: 1.2.3
# Pushes as my-registry.com/team/my-block:1.2.3
polycrate blocks push my-registry.com/team/my-block

Push process

sequenceDiagram
    participant CLI
    participant Workspace
    participant Docker
    participant Registry

    CLI->>Workspace: Read block.poly
    Workspace-->>CLI: Name & Version
    CLI->>CLI: Package block
    CLI->>Docker: Check credentials
    Docker-->>CLI: Credentials OK
    CLI->>Registry: Push manifest
    CLI->>Registry: Push layers
    Registry-->>CLI: Push complete
    CLI-->>CLI: Success

Examples

# Push to your own registry
polycrate blocks push registry.my-org.com/team/my-block

# Push to cargo.ayedo.cloud
polycrate blocks push cargo.ayedo.cloud/my-org/k8s/my-block

# Push to Docker Hub
polycrate blocks push index.docker.io/myuser/my-block

Practical examples

Example 1: Building a team library

# 1. Create a base block (name = full registry path)
mkdir -p my-workspace/blocks/registry.mycompany.com/infra/postgres
cd my-workspace/blocks/registry.mycompany.com/infra/postgres
cat > block.poly <<EOF
name: registry.mycompany.com/infra/postgres
version: 1.0.0
kind: k8sapp
actions:
  - name: install
    playbook: install.yml
EOF

# 2. Push to the team registry
polycrate blocks push registry.mycompany.com/infra/postgres

# 3. Team members can pull
polycrate blocks pull registry.mycompany.com/infra/postgres:1.0.0

Example 2: Multi-registry setup

# workspace.poly with several registries
name: my-workspace

blocks:
  # From cargo.ayedo.cloud (ayedo blocks)
  - name: postgres
    from: cargo.ayedo.cloud/ayedo/k8s/cloudnative-pg:1.3.1

  # From a private company registry
  - name: company-base
    from: registry.mycompany.com/infra/base:2.0.0

  # From Docker Hub
  - name: some-tool
    from: index.docker.io/someuser/tool:1.0.0

Example 3: CI/CD pipeline

# .github/workflows/publish-block.yml
name: Publish Block

on:
  push:
    tags:
      - 'v*'

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2

      - name: Install Polycrate
        run: curl -sSL https://get.polycrate.io | bash

      - name: Docker Login
        run: echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login registry.mycompany.com -u "${{ secrets.REGISTRY_USERNAME }}" --password-stdin

      - name: Push Block
        run: polycrate blocks push registry.mycompany.com/infra/my-block

Example 4: Versioned releases

# Bump the version in block.poly
sed -i 's/version: 1.0.0/version: 1.1.0/' blocks/my-block/block.poly

# Git commit and tag
git add blocks/my-block/block.poly
git commit -m "Bump my-block to v1.1.0"
git tag v1.1.0
git push --tags

# Push to the registry
polycrate blocks push registry.mycompany.com/infra/my-block
# Pushed as my-block:1.1.0

OCI-compatible registries

Polycrate supports all OCI-compatible registries:

Public registries

  • cargo.ayedo.cloud: ayedo-operated registry for Polycrate blocks
  • Docker Hub: index.docker.io
  • GitHub Container Registry: ghcr.io
  • Quay.io: quay.io

Self-hosted registries

  • Harbor: open-source enterprise registry
  • GitLab Container Registry: integrated into GitLab
  • Artifactory: JFrog Artifactory
  • Nexus Repository: Sonatype Nexus
  • Distribution: Docker Registry (Distribution)

Cloud provider registries

  • AWS ECR: <account-id>.dkr.ecr.<region>.amazonaws.com
  • Azure ACR: <registry-name>.azurecr.io
  • Google GCR: gcr.io, eu.gcr.io, us.gcr.io
  • DigitalOcean Registry: registry.digitalocean.com

Block packaging

When you push a block, Polycrate creates an OCI artifact with the following structure:

OCI Artifact: my-block:1.2.3
├── Manifest
│   ├── Config Layer (block.poly Metadata)
│   └── Content Layers
│       ├── block.poly
│       ├── templates/*
│       ├── *.yml (Playbooks)
│       ├── scripts/*
│       ├── README.md
│       └── CHANGELOG.poly

Excluded by default:

  • .specs/ – specifications and development documents (default)

Further directories and files can be configured via exclude patterns.

Exclude patterns on push

When pushing a block, certain directories and files can be excluded from the OCI image. The pattern syntax corresponds to .dockerignore/.gitignore.

Default behavior

By default .specs/ in every block directory is excluded from the push:

# .specs/ is excluded automatically
polycrate blocks push cargo.ayedo.cloud/my-org/my-block

--exclude flag

With the --exclude flag you can specify your own patterns. The flag can be used multiple times or passed with comma-separated values:

# Individual patterns
polycrate blocks push cargo.ayedo.cloud/my-org/my-block \
  --exclude .specs \
  --exclude .git \
  --exclude "*.log"

# Comma-separated
polycrate blocks push cargo.ayedo.cloud/my-org/my-block \
  --exclude ".specs,.git,*.log"

# Exclude a path relative to the block root
polycrate blocks push cargo.ayedo.cloud/my-org/my-block \
  --exclude test/fixtures

Disabling exclusion

To disable all excludes and push the complete block content:

polycrate blocks push cargo.ayedo.cloud/my-org/my-block --exclude ""

Configuration in polycrate.yml

Instead of specifying the flag on every push, a persistent exclusion list can be stored in ~/.polycrate/polycrate.yml:

# ~/.polycrate/polycrate.yml
registry:
  url: cargo.ayedo.cloud
  block_push_exclude:
    - .specs
    - .git
    - "*.log"
    - test/fixtures

The priority is:

  1. --exclude flag (if specified explicitly)
  2. registry.block_push_exclude in polycrate.yml
  3. Default: [".specs"]

Pattern syntax

The syntax fully corresponds to .dockerignore and supports:

Character Meaning Example Matches
* Any characters without path separator *.log error.log, access.log
** Any characters incl. path separators (recursive) logs/** logs/a.log, logs/sub/b.log
? A single arbitrary character access?.log access0.log, accessA.log
[abc] Character class file[123].txt file1.txt, file2.txt

Patterns without / are checked against the base name at every directory depth:

.specs      → .specs/ anywhere in the block directory tree
*.log       → all .log files at every level

Patterns with / are checked against the full relative path from the block root:

test/fixtures   → only test/fixtures/ relative to the block root
logs/**         → everything below logs/
**/build        → every build directory at any depth

Examples

# Default: .specs/ is excluded
polycrate blocks push cargo.ayedo.cloud/my-org/my-block

# Exclude .specs and .git
polycrate blocks push cargo.ayedo.cloud/my-org/my-block \
  --exclude .specs --exclude .git

# Exclude all log files
polycrate blocks push cargo.ayedo.cloud/my-org/my-block \
  --exclude "*.log"

# Exclude a specific subdirectory
polycrate blocks push cargo.ayedo.cloud/my-org/my-block \
  --exclude test/fixtures

# Recursive: exclude everything in docs/
polycrate blocks push cargo.ayedo.cloud/my-org/my-block \
  --exclude "docs/**"

# Verify the content of the pushed image
crane export cargo.ayedo.cloud/my-org/my-block:1.0.0 - | tar t

Best practices

1. Use semantic versioning

# block.poly
name: my-block
version: 1.2.3  # MAJOR.MINOR.PATCH
  • MAJOR: breaking changes (1.x.x → 2.0.0)
  • MINOR: new features, backwards compatible (1.2.x → 1.3.0)
  • PATCH: bug fixes (1.2.3 → 1.2.4)

2. Use versioned tags in from:

# workspace.poly
blocks:
  # ✅ Good: pinned version with full registry path
  - name: my-postgres
    from: cargo.ayedo.cloud/ayedo/k8s/cloudnative-pg:1.3.1

  # ❌ Bad: latest (can break unexpectedly)
  - name: my-postgres-bad
    from: cargo.ayedo.cloud/ayedo/k8s/cloudnative-pg:latest

3. Private registries for proprietary blocks

# Company-internal blocks
polycrate blocks push registry.mycompany.com/infra/proprietary-block

# Public base blocks (from cargo.ayedo.cloud)
polycrate blocks pull cargo.ayedo.cloud/ayedo/k8s/cloudnative-pg:1.3.1

4. Namespace organization

registry.mycompany.com/
├── infra/           # Infrastructure blocks
│   ├── postgres-base
│   ├── redis-base
│   └── nginx-base
├── apps/            # Application blocks
│   ├── webapp-base
│   └── api-base
└── tools/           # Utility blocks
    ├── backup-tools
    └── monitoring-base

Troubleshooting

Authentication failed

Error: authentication required

# Solution: Docker login
docker login cargo.ayedo.cloud
# Or for a custom registry
docker login my-registry.com

Block not found

Error: block 'my-block:1.0.0' not found in registry

# Check whether the block exists
docker manifest inspect cargo.ayedo.cloud/my-org/my-block:1.0.0

# Check available tags
docker search cargo.ayedo.cloud/my-org/my-block

# Use the correct name
polycrate blocks pull cargo.ayedo.cloud/my-org/my-block:1.0.0

Push permissions denied

Error: denied: insufficient permissions

# Solution 1: log in with the correct credentials
docker login registry.mycompany.com -u myuser

# Solution 2: check whether the namespace exists
# The namespace must exist: registry.mycompany.com/namespace/block

# Solution 3: contact the registry admin for permissions

Version conflict

Error: block 'my-block' already exists with different version

# Solution: bump the version in block.poly
sed -i 's/version: 1.0.0/version: 1.0.1/' blocks/my-block/block.poly

# Then push again
polycrate blocks push registry.mycompany.com/infra/my-block

Registry not reachable

Error: failed to connect to registry

# Check the network connection
ping registry.mycompany.com
curl https://registry.mycompany.com/v2/

# Check the Docker configuration
cat ~/.docker/config.json

# Check proxy settings
echo $HTTP_PROXY
echo $HTTPS_PROXY

Relation to other concepts

  • Polycrate Hub: public registry for community blocks
  • Dependencies: blocks can pull dependencies from the registry
  • Inheritance: the from: field references registry blocks
  • Blocks: the registry distributes blocks