Packaging as a Salt extension#
Once your resource type is more than a quick experiment, ship it as a Salt extension rather than carrying it inside core Salt. The framework is designed so an extension's resource types are indistinguishable-at-runtime from in-tree ones: same loader, same targeting, same merge semantics.
Layout#
Mirror the in-tree layout under your extension's package path. Given an
extension called saltext-widgets:
saltext-widgets/
pyproject.toml
src/
saltext/
widgets/
__init__.py
resources/
__init__.py
widget/
__init__.py ← connection module
modules/
__init__.py
test.py
states/
__init__.py
widget.py
grains/
__init__.py
widget.py
Every directory containing Python files needs an __init__.py so
setuptools.find_packages() discovers them. Empty
__init__.py is fine.
pyproject.toml entry point#
The loader discovers extensions via the salt.loader entry point:
[project]
name = "saltext-widgets"
version = "0.1.0"
requires-python = ">=3.10"
dependencies = ["salt>=3008"]
[project.entry-points."salt.loader"]
saltext.widgets = "saltext.widgets"
Salt's loader walks every package registered under salt.loader,
looks for a resources/ subdir, and includes any
<package>/resources/<rtype>/{modules,states,grains}/ directories
in its loader search path ahead of the standard salt locations.
No extra registration step. No "tell Salt about my type". Drop the package on the managing minion, restart, and the type is discoverable.
Where the package gets installed#
Resource extensions need to be importable by the managing minion's
Python interpreter — the same one running salt-minion. For an
onedir installation that's the bundled interpreter; for a system
install it's whatever salt-minion is shebanged with.
Onedir:
salt-pip install saltext-widgetsSystem:
pip install saltext-widgetsin the same environment assalt-minionContainer: bake the extension into your minion image; don't expect
saltutil.pillar_refreshto install new types at runtime.
Discovering what shipped#
Once installed, the managing minion will load the type the next time
its pillar refresh discovers a matching entry. On the master,
salt-run resource.list_grains confirms the minion registered
resources of the new type.
Distribution checklist#
When publishing the extension:
pyproject.tomllistssalt>=3008as a dependency.Each resource type ships at least one execution test — the dummy type is a fine template.
The README documents the pillar shape the type expects (key names, required vs optional fields).
If your type talks to a remote service, document the credentials model and how to wire pillar so secrets are decrypted at compile time, not stored in plaintext.
Working example#
saltext-opsdev is the
canonical real-world example: it ships two resource types
(starting_state, nimbus_testbed), per-type execution and state
overrides, and integration tests. Mirror its layout when in doubt.