Working with Subpackages
Independent subpackages allow you to compose a single package from multiple upstream sources, where each subpackage can be cloned and upgraded independently. This guide walks through the complete workflow of adding an independent subpackage to an existing package and then upgrading it when a new upstream version becomes available.
For the conceptual overview, see Subpackages. For detailed command reference, see the porchctl CLI guide.
Key Concepts
- Independent subpackage: A kpt package nested in a subdirectory of a parent package that maintains its own
upstream tracking via its
Kptfile. See the kpt package documentation for a full description of independent subpackages - Parent package revision: The Draft package revision into which subpackages are cloned or upgraded.
- Subpackage directory: The relative path within the parent package where the subpackage resides.
Unlike a regular clone (which creates a new package revision), a subpackage clone adds content into an existing Draft package revision. Similarly, a subpackage upgrade modifies the parent package revision in-place rather than creating a new one.
Prerequisites
Before following this guide, ensure you have:
- A Kubernetes cluster running
porchctlinstalled- At least one repository registered with published upstream packages to clone from
- A Draft package revision that will serve as the parent package
Subpackage operations require the parent package revision to be in Draft state.
Subpackage operations may be performed on a freshly created draft before any other modifications are pushed to it or on a draft to which other modifications have already been pushed. Multiple subpackages may be cloned and upgraded on a draft one after another.
End-to-End Example
This example demonstrates:
- Creating a parent package
- Cloning an upstream package into it as an independent subpackage
- Publishing the parent package
- Upgrading the subpackage when a new upstream version is available
Step 1: Set Up Upstream Blueprints
First, ensure you have published upstream packages to clone from. In this example we assume two published blueprint revisions exist:
$ porchctl rpkg get --namespace=porch-demo --name=networking
NAME PACKAGE WORKSPACENAME REVISION LATEST LIFECYCLE REPOSITORY
blueprints.networking.main networking main -1 false Published blueprints
blueprints.networking.v1 networking v1 1 false Published blueprints
blueprints.networking.v2 networking v2 2 true Published blueprints
Step 2: Create a Parent Package Draft
Create a new package that will contain the subpackage:
# Initialize a parent package
porchctl rpkg init my-composed-app --namespace=porch-demo --repository=deployments --workspace=v1
Verify the draft was created:
$ porchctl rpkg get --namespace=porch-demo --name=my-composed-app
NAME PACKAGE WORKSPACENAME REVISION LATEST LIFECYCLE REPOSITORY
deployments.my-composed-app.v1 my-composed-app v1 0 false Draft deployments
Step 3: Clone a Subpackage into the Parent
Clone the upstream networking blueprint into a subdirectory of the parent package:
porchctl rpkg clone \
blueprints.networking.v1 \
deployments.my-composed-app.v1 \
--subpackage-dir=components/networking \
--namespace=porch-demo
This clones the contents of blueprints.networking.v1 into the components/networking directory within the
parent package revision deployments.my-composed-app.v1.
Expected output:
subpackage cloned into directory "components/networking" in package revision "deployments.my-composed-app.v1"
You can verify by pulling the parent package locally:
porchctl rpkg pull deployments.my-composed-app.v1 ./my-composed-app --namespace=porch-demo
The directory structure will include:
my-composed-app/
├── Kptfile
├── package-context.yaml
└── components/
└── networking/
├── Kptfile # Subpackage's own Kptfile with upstream info
└── *.yaml # Subpackage resources
Step 4: Publish the Parent Package
Once you’re satisfied with the composed package, propose and approve it:
porchctl rpkg propose deployments.my-composed-app.v1 --namespace=porch-demo
porchctl rpkg approve deployments.my-composed-app.v1 --namespace=porch-demo
Step 5: Upgrade the Subpackage
When a new upstream version (blueprints.networking.v2) becomes available, you can upgrade the subpackage.
First, create a new draft of the parent package:
porchctl rpkg copy deployments.my-composed-app.v1 --namespace=porch-demo --workspace=v2
Then upgrade the subpackage within the new draft:
porchctl rpkg upgrade deployments.my-composed-app.v2 \
--subpackage-dir=components/networking \
--revision=2 \
--namespace=porch-demo
Expected output:
independent subpackage in directory "components/networking" in package "deployments.my-composed-app.v2" upgraded
The subpackage at components/networking is now upgraded to the contents of blueprints.networking.v2, merged
with any local customizations using the chosen strategy (default: resource-merge).
Finally, publish the upgraded parent:
porchctl rpkg propose deployments.my-composed-app.v2 --namespace=porch-demo
porchctl rpkg approve deployments.my-composed-app.v2 --namespace=porch-demo
Subpackage Clone in Detail
Usage
porchctl rpkg clone SOURCE_PACKAGE PARENT_PACKAGE_REVISION \
--subpackage-dir=<path> \
--namespace=<namespace>
When --subpackage-dir is specified:
SOURCE_PACKAGEis the upstream package revision to clone (e.g.,blueprints.networking.v1)PARENT_PACKAGE_REVISION(theNAMEargument) is the existing Draft package revision that will receive the subpackage--repositoryand--workspacemust not be specified- The subdirectory must not already exist in the parent package
Constraints
| Requirement | Reason |
|---|---|
| Parent must be in Draft state | Subpackage clone modifies an existing package revision |
| Subdirectory must not exist | Prevents overwriting existing content |
Path must be relative (no leading or trailing /, no ./ or ..) |
Ensures subpackage stays within the parent package tree |
Subpackage Upgrade in Detail
Usage
porchctl rpkg upgrade PARENT_PACKAGE_REVISION \
--subpackage-dir=<path> \
[--revision=<number>] \
[--strategy=<strategy>] \
--namespace=<namespace>
When --subpackage-dir is specified:
PARENT_PACKAGE_REVISIONis the Draft package revision containing the subpackage--workspacemust not be specified (the upgrade modifies the parent in-place)- The subdirectory must already exist and contain a valid
Kptfilewith upstream information --revisionspecifies the upstream revision to upgrade to (if omitted, upgrades to latest)--strategycontrols the merge behaviour (default:resource-merge)
How Porch Determines the Upstream
During a subpackage upgrade, Porch:
- Reads the
Kptfileat the specified subdirectory within the parent package - Extracts the upstream Git repository, package name, and current revision from the
Kptfile - Finds the matching registered repository in Porch
- Locates the old upstream package revision (current version) and the new upstream package revision (target version)
- Performs a merge of the new upstream into the subpackage directory
Constraints
| Requirement | Reason |
|---|---|
| Parent must be in Draft state | Subpackage upgrade modifies an existing package revision |
| Subdirectory must exist with a valid Kptfile | The subpackage’s upstream info is read from the Kptfile |
| Upstream repository must be registered in Porch | Porch needs to resolve package revisions for the merge |
--workspace must not be set |
The upgrade operates in-place on the parent, not creating a new package revision |
Merge Strategies for Subpackage Upgrades
The same merge strategies available for regular upgrades apply to subpackage upgrades:
| Strategy | Behaviour |
|---|---|
resource-merge (default) |
Structural 3-way merge preserving local customizations |
copy-merge |
Upstream files overwrite local; local-only files are preserved |
force-delete-replace |
Completely replaces subpackage contents with upstream |
fast-forward |
Fails if local modifications exist |
Example with a specific strategy:
porchctl rpkg upgrade deployments.my-composed-app.v2 \
--subpackage-dir=components/networking \
--revision=2 \
--strategy=copy-merge \
--namespace=porch-demo
Troubleshooting
Clone fails with “parent package must be in state draft”?
- Ensure the target parent package revision is in Draft state
- If it’s Published, create a new draft with
porchctl rpkg copyfirst
Clone fails with “invalid –subpackage-dir”?
- The path must be a relative directory without leading
/,./, or..segments - Example valid paths:
components/networking,subpkgs/monitoring,infra
Upgrade fails with “could not find Kptfile for independent subpackage”?
- Verify the subdirectory exists in the parent package and contains a
Kptfile - Pull the parent package locally to inspect:
porchctl rpkg pull <parent> ./dir --namespace=<ns>
Upgrade fails with “subpackage is not managed by kpt and cannot be upgraded”?
- The subpackage’s
Kptfiledoes not have valid upstream Git information - The subpackage may have been manually created rather than cloned via Porch
Upgrade fails with “could not find repository”?
- The upstream Git repository referenced in the subpackage’s
Kptfileis not registered in Porch - Register the upstream repository with
porchctl repo register
"–workspace may not be specified on subpackage upgrades/clones"?
- Remove the
--workspaceflag; subpackage operations modify the parent in-place
"–repository may not be specified on subpackage clones"?
- Remove the
--repositoryflag; the target is the parent package revision, not a repository