Artifacts
Workspace artifacts in Polycrate: the artifacts/ directory structure, block artifact directories, the secrets directory, standard artifacts such as inventory and kubeconfig, using and generating artifacts in actions, and the migration from Artifact Discovery to template blocks.
Artifacts
| Concept | Status | Where documented? |
|---|---|---|
| **Workspace artifacts** (files in the `artifacts/` folder per block during action execution) | **Active – full feature support continues** | **This page** |
| **Artifact Discovery / Embedded CRDs / ArtifactRepository** (CRs managed by the operator on a per-block basis) | **Removed in CLI 0.34.0** – replaced by [template blocks](/docs/polycrate/blocke/) in the [PolyHub](/docs/polycrate/polyhub/) | Migration note below |
If you landed on this page because you were looking for the **Artifact Discovery** feature: see the section [Migration to template blocks](#migration-artifact-discovery-template-blocks) at the end.
What are artifacts?
Artifacts are files that are generated, required or persisted during the execution of actions. They live per block under artifacts/blocks/<block>/ and are preserved between action runs; there is no shared artifact storage between different blocks.
Typical artifacts are:
- Ansible inventories - dynamically generated or copied host lists
- Kubeconfigs - Kubernetes cluster access configurations
- Secrets - encrypted sensitive files (passwords, certificates, keys)
- SSH keys - SSH key pairs for remote access
- Generated manifests - YAML/JSON files for Kubernetes or other systems
- Certificates - TLS certificates, CA bundles
- Backup files - database dumps, configuration backups
Artifacts structure
| Path | Description | Sensitive |
|---|---|---|
inventory.yml |
Ansible inventory (workspace root) | No |
artifacts/ |
Artifacts root | - |
artifacts/secrets/ |
Workspace-wide secrets | ⚠️ Yes |
artifacts/secrets/id_rsa |
SSH private key | ⚠️ Yes |
artifacts/secrets/id_rsa.pub |
SSH public key | No |
artifacts/secrets/kubeconfig.yml |
Kubernetes config | ⚠️ Yes |
artifacts/secrets/<block>/ |
Block-specific secrets | ⚠️ Yes |
artifacts/blocks/ |
Block artifacts | No |
artifacts/blocks/<block>/generated/ |
Generated manifests | No |
artifacts/blocks/<block>/hosts_cache.json was formerly used as an SSH host cache; current Polycrate versions no longer create this file (existing workspaces may still contain it).
Default directory structure
my-workspace/
├── inventory.yml # 📋 Ansible inventory (workspace root, YAML)
├── artifacts/ # Artifacts root (configurable)
│ ├── secrets/ # 🔐 Encrypted secrets
│ │ ├── id_rsa # Workspace SSH key (encrypted)
│ │ ├── id_rsa.pub # Workspace SSH pubkey
│ │ ├── kubeconfig.yml # Kubernetes kubeconfig (encrypted)
│ │ └── my-app/ # Block-specific secrets
│ │ ├── database.pwd # Database password
│ │ └── api-key.txt # API key
│ └── blocks/ # Block artifacts
│ ├── my-cluster/
│ │ └── cluster-info.json
│ ├── infrastructure/
│ │ └── (block-specific artifacts)
│ └── my-app/
│ └── generated/
│ ├── deployment.yml
│ └── configmap.yml
├── secrets.poly.age # 🔐 Encrypted secrets.poly
└── blocks/ # Block definitions
Configuring the artifacts root
The artifacts root directory can be configured in workspace.poly. The YAML key is artifactsroot (one word, no underscore), not artifacts_root.
name: my-workspace
config:
artifactsroot: artifacts # Default
# or a different path:
# artifactsroot: .artifacts
# artifactsroot: /tmp/polycrate-artifacts
Alternatively via the global CLI flag (sets workspace.config.artifactsroot):
polycrate --artifacts-root /custom/path run my-block install
Block artifacts directory
A block artifacts directory is created automatically for every block:
artifacts/blocks/<BLOCK_NAME>/
This directory:
- Is created automatically when the block is first loaded
- Is persistent between action executions
- Is mounted into the container
- Is part of the snapshot
Block artifact variables in Ansible/Jinja
The following snapshot variables are available in playbooks and Jinja templates:
| Variable | Description | Example |
|---|---|---|
block.artifacts.path |
Generic path | artifacts/blocks/my-app |
block.artifacts.localpath |
Local path (host) | /abs/path/artifacts/blocks/my-app |
block.artifacts.containerpath |
Path in the Ansible container | /polycrate/artifacts/blocks/my-app |
block.artifacts.secrets.path |
Path to the secrets folder | artifacts/blocks/my-app/secrets |
block.artifacts.secrets.localpath |
Local path to the secrets folder | /abs/path/artifacts/blocks/my-app/secrets |
block.artifacts.secrets.containerpath |
Container path to the secrets folder | /polycrate/artifacts/blocks/my-app/secrets |
```yaml
- name: Copy SSH key from block secrets
copy:
src: "{{ block.artifacts.secrets.localpath }}/id_rsa"
dest: /tmp/deploy-key
mode: '0600'
```
Secrets directory
The secrets directory (artifacts/secrets/) is a special area for sensitive files that are encrypted automatically during workspace encryption.
Structure
artifacts/secrets/
├── id_rsa # Workspace SSH key
├── id_rsa.pub # Workspace SSH pubkey
├── kubeconfig.yml # Workspace kubeconfig
└── <block-name>/ # Block-specific secrets
├── database.pwd # Password file
├── api-key.txt # API key
└── cert.pem # Certificate
Workspace secrets vs. block secrets
| Type | Path | Access in Ansible |
|---|---|---|
| Workspace secrets | artifacts/secrets/ |
{{ workspace.secrets['filename'] }} |
| Block secrets | artifacts/secrets/<block>/ |
{{ block.secrets['filename'] }} |
Example: using secrets in Ansible
# deploy.yml
- name: Deploy with secrets
hosts: all
tasks:
# Use workspace secret
- name: Kubeconfig from workspace secrets
shell: kubectl --kubeconfig={{ workspace.secrets['kubeconfig.yml'] }} get pods
# Use block secret
- name: Database password from block secrets
shell: mysql -p$(cat {{ block.secrets['database.pwd'] }}) -h localhost
Encryption
All files in artifacts/secrets/ are encrypted automatically when you encrypt the workspace:
# Encrypt workspace (incl. secrets)
polycrate workspace encrypt
# Show status
polycrate workspace status
# Decrypt for editing
polycrate workspace decrypt
After encryption:
secrets.poly→secrets.poly.ageartifacts/secrets/id_rsa→artifacts/secrets/id_rsa.ageartifacts/secrets/kubeconfig.yml→artifacts/secrets/kubeconfig.yml.age
→ Detailed documentation on workspace encryption
Standard artifacts
1. Ansible inventory
The Ansible inventory lives as inventory.yml directly in the workspace root. It must be in YAML format — INI format is not supported. Polycrate sets ANSIBLE_INVENTORY to this path automatically.
# inventory.yml (workspace root)
all:
hosts:
web1:
ansible_host: 192.168.1.10
ansible_user: deploy
ansible_port: 22
web2:
ansible_host: 192.168.1.11
ansible_user: deploy
vars:
ansible_python_interpreter: /usr/bin/python3
children:
webservers:
hosts:
web1: {}
web2: {}
2. Kubeconfig
The kubeconfig must be stored under artifacts/secrets/kubeconfig.yml. This path is inside the secrets directory and is therefore automatically covered by workspace encryption.
# artifacts/secrets/kubeconfig.yml
apiVersion: v1
kind: Config
clusters:
- name: prod-cluster
cluster:
server: https://k8s.example.com:6443
certificate-authority-data: LS0tLS...
contexts:
- name: prod
context:
cluster: prod-cluster
user: admin
namespace: production
current-context: prod
users:
- name: admin
user:
client-certificate-data: LS0tLS...
client-key-data: LS0tLS...
3. SSH host cache (historical)
Earlier Polycrate versions could cache SSH connection data under artifacts/blocks/<block>/hosts_cache.json. Current releases no longer create this file. The file may still exist in older workspaces; it is no longer actively managed.
Using artifacts in actions
In Ansible playbooks
Artifacts are automatically available via snapshot variables:
---
- name: Deploy Application
hosts: localhost
connection: local
tasks:
- name: Load generated manifest
set_fact:
manifest: "{{ lookup('file', block.artifacts.path + '/generated/deployment.yml') }}"
- name: Apply Manifest
kubernetes.core.k8s:
state: present
definition: "{{ manifest }}"
Best practice: Ansible playbooks
All actions should be implemented with Ansible playbooks:
actions:
- name: status
playbook: status.yml
- name: deploy
playbook: deploy.yml
In the playbook you have access to all workspace and block variables via Jinja2 templating.
Generating artifacts
Example 1: generating a kubeconfig
Complex artifact generation should happen in an Ansible playbook:
# block.poly
actions:
- name: create-cluster
playbook: create-cluster.yml
- name: get-kubeconfig
playbook: get-kubeconfig.yml
# get-kubeconfig.yml
- name: Fetch and store kubeconfig
hosts: localhost
gather_facts: false
tasks:
- name: Export kubeconfig
shell: kubectl config view --flatten
register: kubeconfig_output
- name: Store kubeconfig as artifact
copy:
content: "{{ kubeconfig_output.stdout }}"
dest: "{{ block.artifacts.path }}/kubeconfig.yml"
mode: '0600'
- name: Confirmation
debug:
msg: "Kubeconfig saved to {{ block.artifacts.path }}/kubeconfig.yml"
The workspace kubeconfig for Kubernetes actions, by contrast, lives at the fixed location artifacts/secrets/kubeconfig.yml (see section Kubeconfig). Another block entry in workspace.poly can refer to the kubeconfig context of another block via kubeconfig.from: – this is configuration wiring, not a shared artifact directory:
# workspace.poly
blocks:
- name: my-cluster
from: registry.my-org.com/blocks/k8s/cluster:1.0.0
- name: my-app
from: registry.my-org.com/blocks/k8s/my-app:1.0.0
kubeconfig:
from: my-cluster # Uses kubeconfig.yml of block "my-cluster"
Example 2: generating an inventory from a cloud API
# generate-inventory.yml
- name: Generate inventory from cloud API
hosts: localhost
gather_facts: false
tasks:
- name: Fetch cloud instances
amazon.aws.ec2_instance_info:
region: "{{ block.config.region }}"
filters:
"tag:Environment": "{{ block.config.environment }}"
register: ec2_instances
- name: Generate inventory
template:
src: inventory.yml.j2
dest: "{{ block.artifacts.path }}/inventory.yml"
vars:
hosts: "{{ ec2_instances.instances }}"
Example 3: persisting generated configuration
# generate-config.yml
- name: Generate and persist configuration
hosts: localhost
gather_facts: false
tasks:
- name: Generate configuration
template:
src: config.yml.j2
dest: "{{ block.artifacts.path }}/config.yml"
vars:
namespace: "{{ block.config.namespace }}"
replicas: "{{ block.config.replicas }}"
- name: Create cache file
copy:
content: "{{ block.config | to_nice_json }}"
dest: "{{ block.artifacts.path }}/config_cache.json"
No artifact sharing between blocks
Artifacts are stored per block under artifacts/blocks/<block-name>/. There is no shared artifact repository: a block cannot directly read or "take over" the artifact files of another block, and workflows do not automatically pass artifact files between steps or blocks either.
What is possible in workspace.poly (e.g. kubeconfig.from:, inventory.from:) are references to other block instances for the workspace configuration (which context, which inventory applies) – not the simultaneous use of a shared artifact folder. Data that one block needs for the next must be provided via workspace-wide paths (e.g. inventory.yml, artifacts/secrets/) or by regenerating it in the target block.
Best practices
1. Artifacts for reproducible builds
Store all generated files as artifacts (in a playbook):
# build.yml
- name: Build and store artifacts
hosts: localhost
gather_facts: false
tasks:
- name: Run build
command: make build
- name: Create artifacts directory
file:
path: "{{ block.artifacts.path }}/build"
state: directory
- name: Copy build artifacts
copy:
src: "{{ item }}"
dest: "{{ block.artifacts.path }}/build/"
loop:
- dist/
- build.log
2. Git and artifacts
artifacts/ contains reproducible outputs (manifests, generated inventories, block artifacts). These are part of the workspace repository and should usually be versioned so that GitOps and review processes remain traceable.
Volatile or large intermediate states (npm/pip caches, temporary downloads) do not belong under artifacts/ in the workspace: put such paths under /tmp in the container or outside of workspace.path, or clean them up in an action.
Secrets: do not commit unencrypted secrets – see Workspace encryption.
3. Artifacts cleanup
Implement cleanup actions as a playbook:
# clean.yml
- name: Clean up artifacts
hosts: localhost
gather_facts: false
tasks:
- name: Empty artifacts directory
file:
path: "{{ block.artifacts.path }}"
state: absent
- name: Recreate artifacts directory
file:
path: "{{ block.artifacts.path }}"
state: directory
- name: Confirmation
debug:
msg: "Artifacts cleaned: {{ block.artifacts.path }}"
4. Document artifacts
Document which artifacts a block creates (in the block README):
# my-block
## Generated artifacts
| File | Description |
|-------|-------------|
| `kubeconfig.yml` | Kubernetes cluster access |
| `inventory.yml` | Ansible host list |
| `certs/ca.crt` | Root CA |
| `certs/tls.crt` | Server certificate |
| `certs/tls.key` | Private key (encrypted) |
5. Validate artifacts
Validate artifacts after generation:
# generate-inventory.yml
- name: Generate and validate inventory
hosts: localhost
gather_facts: false
tasks:
- name: Generate inventory
command: python3 scripts/generate-inventory.py
register: inventory_output
- name: Store inventory
copy:
content: "{{ inventory_output.stdout }}"
dest: "{{ block.artifacts.path }}/inventory.yml"
- name: Validate inventory
command: ansible-inventory -i {{ block.artifacts.path }}/inventory.yml --list
register: validation
- name: Validation successful
debug:
msg: "Inventory validated: {{ (validation.stdout | from_json).all.hosts | length }} hosts"
Artifacts in the lifecycle
sequenceDiagram
participant User
participant CLI
participant Block
participant Artifacts
User->>CLI: polycrate run block provision
CLI->>Artifacts: Load existing artifacts
Artifacts-->>CLI: inventory.yml, kubeconfig.yml
CLI->>Block: Execute action
Block->>Artifacts: Generate new artifacts
Artifacts-->>Block: Stored
Block-->>CLI: Action successful
CLI->>Artifacts: Workspace reload: load new artifacts
Artifacts-->>CLI: Updated artifacts
CLI-->>User: Done
User->>CLI: polycrate run app deploy
CLI->>Artifacts: Load artifacts (incl. newly generated)
Artifacts-->>CLI: inventory.yml, kubeconfig.yml
CLI->>Block: Execute action with artifacts
- Before the action: existing artifacts are loaded
- During the action: new artifacts are generated/modified
- After the action: the workspace is reloaded (new artifacts available)
- Next action: uses the updated artifacts
Relation to other concepts
- Snapshot: artifacts are part of the snapshot
- Inheritance: block artifacts and configurations can be inherited
- Workflows: workflows execute actions in a defined order (no automatic artifact sharing between blocks)
- Container: artifacts are mounted into the container
Migration: Artifact Discovery → Template Blocks
The second, discontinued artifact concept was Artifact Discovery / ArtifactRepository / Embedded CRDs – a mechanism in which blocks additionally shipped Kubernetes CRs ("embedded artifacts") that the Polycrate Operator collected via an ArtifactRepository and an ArtifactDiscovery controller and applied to clusters.
Status
- Removed in Polycrate CLI 0.34.0 (see release notes)
- Removed in Polycrate API 0.15.0 – the operator no longer knows
ArtifactRepository/ArtifactDiscovery - Data model
ArtifactRepositoryand the associated Celery tasks have been completely removed from the API
Why was it replaced?
| Problem | Solution with template blocks |
|---|---|
| Embedded CRDs were invisible (hidden in block source files) | Template blocks are standalone, versioned objects in the PolyHub |
| No clean versioning of individual artifacts possible | Every template block has its own versions and changelogs |
| The operator had to clone blocks just to read CRs | The operator installs template blocks directly from the Polycrate API |
| No UI for browsing | The PolyHub offers a full browsing UI, filters, readmes, changelog |
Migration path
- Inventory: which embedded artifacts were shipped in existing blocks?
- Split: every embedded artifact becomes its own template block in the PolyHub – typically under the appropriate namespace (
ayedo/k8s/<name>). - Block release: template block with its own
block.poly,README.md,CHANGELOG.polyand manifests → push to the PolyHub viapolycrate blocks push. - Polycrate API: create the PolyHub as a DataSource of kind
polycrate-hub– the template blocks then appear in Polycrate automatically. - Operator: cluster/app installs template blocks directly from the API in future (instead of from an ArtifactRepository).
- Slim down the old block: remove the
artifacts/sections with CRs from the old block; only the actual install logic remains.