Inheritance
Block inheritance in Polycrate: how block instances in workspace.poly inherit from template blocks via from:, config deep merge, overriding actions, inventory and kubeconfig conventions and best practices.
Inheritance
What is block inheritance?
Block inheritance enables the reuse and customization of existing blocks. A block instantiated in workspace.poly can inherit from a template block and take over its properties:
- Configuration (
config) – merged via deep merge - Actions – can be overridden or extended
- Working directory – set to the template block
Inheritance hierarchy
+-----------------------------+
| blocks/ Template block |
| postgres-base/block.poly |
+-----------------------------+
|
| from: postgres-base
v
+-----------------------------+
| workspace.poly |
| Block instance: my-postgres |
+-----------------------------+
Important: There is only ONE level of inheritance:
- Template block (
blocks/postgres-base/block.poly) – defines actions, config defaults, playbooks - Block instance (
workspace.poly) – inherits viafrom:and overrides where needed
Defining inheritance
Template block (block.poly)
A template block defines the base functionality:
# blocks/postgres-base/block.poly
name: postgres-base
kind: k8sapp
version: 1.0.0
config:
namespace: default
app:
persistence:
size: 10Gi
storageClass: standard
replicas: 1
actions:
- name: install
playbook: install.yml
- name: backup
playbook: backup.yml
Block instance (workspace.poly)
In workspace.poly the template block is instantiated and customized:
# workspace.poly
blocks:
- name: my-postgres
from: postgres-base # Inherits from local template block
config:
namespace: production # Override
app:
persistence:
size: 100Gi # Override
- name: harbor
from: registry.my-org.com/blocks/k8s/harbor:1.2.0 # Inherits from registry block
config:
namespace: harbor-prod
Merge behavior
When merging the configurations, the following applies:
Template block config Block instance config
\ /
\ /
-------- Deep Merge --------
|
v
Resulting config
Important: Instance values override template values!
Example: config merge
Template block (blocks/postgres-base/block.poly):
name: postgres-base
config:
namespace: default
app:
persistence:
size: 10Gi
storageClass: standard
replicas: 1
Block instance (workspace.poly):
blocks:
- name: my-postgres
from: postgres-base
config:
namespace: production
app:
persistence:
size: 50Gi
Resulting config (workspace snapshot):
# my-postgres after merge
config:
namespace: production # overridden
app:
persistence:
size: 50Gi # overridden
storageClass: standard # inherited
replicas: 1 # inherited
Inheriting and overriding actions
Actions are inherited as well, but can be overridden in the block instance:
Template block (blocks/postgres-base/block.poly):
actions:
- name: install
playbook: install.yml
- name: backup
playbook: backup.yml
Block instance (workspace.poly):
blocks:
- name: my-postgres
from: postgres-base
actions:
- name: install
playbook: custom-install.yml # Overrides the template action
- name: migrate
playbook: migrate.yml # New action
Result: The block instance my-postgres has three actions:
install(overridden with custom-install.yml)backup(inherited from the template block)migrate(newly added)
Inventory and Kubeconfig
Recommended convention (current)
inventory.ymlin the workspace root – one shared Ansible inventory for the entire workspace (default file name, see workspace configuration for deviations).artifacts/secrets/kubeconfig.yml– one workspace-wide kubeconfig; the CLI detects the file and provides the paths in the snapshot.
This keeps the scope per workspace clearly delimited. → Best practices: Inventory and Kubeconfig
kubeconfig.from / inventory.from (legacy)
Optionally, block instances in workspace.poly can set kubeconfig.from or inventory.from and thereby refer to the artifacts of other block instances (not to their config: – that is a different mechanism than from:).
# workspace.poly (pattern – not recommended from a best-practice perspective)
blocks:
- name: my-cluster
from: registry.acme.corp/acme/k8s/cluster:1.0.0
- name: my-app
from: registry.acme.corp/acme/k8s/my-app:1.0.0
kubeconfig:
from: my-cluster # Kubeconfig artifact from block "my-cluster"
Best practices
1. Create template blocks
Create reusable template blocks in the blocks/ directory:
# blocks/postgres-template/block.poly
name: postgres-template
kind: k8sapp
version: 1.0.0
config:
# Sensible defaults
namespace: default
persistence:
enabled: true
size: 10Gi
monitoring:
enabled: true
actions:
- name: install
playbook: install.yml
- name: backup
playbook: backup.yml
2. Sensible overrides in workspace.poly
Override only what is necessary:
# workspace.poly
blocks:
# ✅ Good: override only specific values
- name: my-postgres-prod
from: postgres-template
config:
namespace: production
persistence:
size: 100Gi
# ❌ Bad: repeat the entire config
- name: my-postgres-bad
from: postgres-template
config:
namespace: production
persistence:
enabled: true # Unnecessary, is inherited
size: 100Gi
monitoring:
enabled: true # Unnecessary, is inherited
3. Use registry blocks
For production-ready blocks we recommend using registry blocks:
# workspace.poly
blocks:
- name: my-harbor
from: registry.my-org.com/blocks/k8s/harbor:1.2.0
config:
namespace: harbor-production
Polycrate automatically pulls the block from the registry and uses it as the template.