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:
- Polycrate checks every block registry URL against all
proxyentries - On a match,
fromis replaced bytoin the URL - Optionally, per-proxy credentials can be specified
- 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:
- Specific patterns first: sort proxies from specific to generic
- Credentials management: use
secrets.polyfor passwords ordocker login - Registry mirroring: make sure the proxy registry contains the blocks
- Testing: test the proxy configuration with
--loglevel 2for 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:
--excludeflag (if specified explicitly)registry.block_push_excludeinpolycrate.yml- 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