Manual Migration Process
Please ensure you have followed the installation guide
to install the required components and have the desired version of the
Kubebuilder CLI available in your PATH.
This guide outlines the manual steps to migrate your existing Kubebuilder project to a newer version of the Kubebuilder framework. This process involves re-scaffolding your project and manually porting over your custom code and configurations.
From Kubebuilder v3.0.0 onwards, all inputs used by Kubebuilder are tracked in the PROJECT file.
Ensure that you check this file in your current project to verify the recorded configuration and metadata.
Review the PROJECT file documentation for a better understanding.
Also, before starting, it is recommended to check What’s in a basic project? to better understand the project layouts and structure.
Step 1: Prepare Your Current Project
Before starting the migration, you need to backup your work and identify key project information.
1.1 Create a backup branch
Create a branch from your current codebase to preserve your work:
git checkout -b migration
1.2 Create a backup
Create a directory to hold all your current project files as a backup:
mkdir ../migration-backup
cp -r . ../migration-backup/
1.3 Clean your project directory
Remove all files except .git from your current project directory to start fresh:
find . -not -path './.git*' -not -name '.' -not -name '..' -delete
Step 2: Initialize the New Project
You have two options: use alpha generate if your project has a PROJECT file, or manually initialize.
Option B: Manual Initialization
If alpha generate doesn’t work or you prefer manual control, follow these steps:
2.1 Identify your module and domain
First, identify the information you’ll need for initialization. You can compare with your main branch or check the backup directory.
Module path - Check your go.mod file:
# Compare with main branch
git show main:go.mod
Look for the module line:
module tutorial.kubebuilder.io/migration-project
Domain - Check your PROJECT file (if it exists):
# Compare with main branch
git show main:PROJECT
Look for the domain line:
domain: tutorial.kubebuilder.io
If you don’t have a PROJECT file (versions < v3.0.0),
check your CRD files under config/crd/bases/ or examine the API group names.
The domain is the part after the group name in your API groups.
2.2 Initialize the Go module
Initialize a new Go module using the same module path from your original project:
go mod init tutorial.kubebuilder.io/migration-project
Replace tutorial.kubebuilder.io/migration-project with your actual module path.
2.3 Initialize Kubebuilder project
Initialize the project with Kubebuilder:
kubebuilder init --domain tutorial.kubebuilder.io --repo tutorial.kubebuilder.io/migration-project
Replace with your actual domain and repository (module path).
2.4 Enable multi-group support (if needed)
Multi-group projects organize APIs into different groups, with each group in its own directory. This is useful when you have APIs for different purposes or domains.
Check if your project uses multi-group layout by examining your backup’s directory structure:
-
Single-group layout: All APIs in one group
api/v1/cronjob_types.goapi/v1/job_types.goapi/v2/cronjob_types.go
-
Multi-group layout: APIs organized by group
api/batch/v1/cronjob_types.goapi/crew/v1/captain_types.goapi/sea/v1/ship_types.go
You can also check your backup’s PROJECT file for:
multigroup: true
If your project uses multi-group layout, enable it before creating APIs:
kubebuilder edit --multigroup=true
When following this guide for any to latest migration, you’ll naturally get the new layout since you’re creating a fresh v4 project and porting your code into it.
Step 3: Re-scaffold APIs and Controllers
For each API resource in your original project, re-scaffold them in the new project.
3.1 Identify all your APIs
Review your backup project (../migration-backup/) to identify all APIs. It’s recommended to check the backup directory
regardless of whether you have a PROJECT file, as not all resources may have been created using the CLI.
Check the directory structure in your backup to ensure you don’t miss any manually created resources:
-
Look in the
api/directory for*_types.gofiles:- Single-group:
api/v1/cronjob_types.go→ extract: versionv1, kindCronJob, group from imports - Multi-group:
api/batch/v1/cronjob_types.go→ extract: groupbatch, versionv1, kindCronJob
- Single-group:
-
Check for controllers. The location depends on the Kubebuilder version:
- Newer versions (v3+):
internal/controller/cronjob_controller.go - Older versions:
controllers/cronjob_controller.go - A file like
cronjob_controller.goindicates a controller exists for that kind
- Newer versions (v3+):
If you used the CLI to create all APIs from Kubebuilder v3.0.0+ you should have then in the PROJECT file under the resources section, such as:
resources:
- api:
crdVersion: v1
namespaced: true
controller: true
group: batch
kind: CronJob
version: v1
3.2 Create each API and Controller
For each API identified in step 3.1, re-scaffold it:
kubebuilder create api --group batch --version v1 --kind CronJob
When prompted:
- Answer yes to “Create Resource [y/n]” to generate the API types
- Answer yes to “Create Controller [y/n]” if your original project has a controller for this API
After creating each API, update the generated manifests and code:
make manifests # Generate CRD, RBAC, and other config files
make generate # Generate code (e.g., DeepCopy methods)
Then verify everything compiles:
make build
These steps ensure the newly scaffolded API is properly integrated. See the Quick Start guide for a detailed walkthrough of the API creation workflow.
Repeat this process for ALL APIs in your project.
After creating all resources, regenerate manifests:
make manifests
make generate
3.3 Re-scaffold webhooks (if applicable)
If your original project has webhooks, you need to re-scaffold them.
Identify webhooks in your backup project:
-
From directory structure, look for webhook files:
- Legacy location:
api/v1/<kind>_webhook.goorapi/<group>/<version>/<kind>_webhook.go - Current location:
internal/webhook/<version>/<kind>_webhook.go
- Legacy location:
-
From
PROJECTfile (if available), check each resource’s webhooks section:
resources:
- api:
...
webhooks:
defaulting: true
validation: true
webhookVersion: v1
Re-scaffold webhooks:
For each resource with webhooks, run:
kubebuilder create webhook --group batch --version v1 --kind CronJob --defaulting --programmatic-validation
Webhook options:
--defaulting- creates a defaulting webhook (sets default values)--programmatic-validation- creates a validation webhook (validates create/update/delete operations)--conversion- creates a conversion webhook (for multi-version APIs, see next section)
3.4 Re-scaffold conversion webhooks (if applicable)
If your project has multi-version APIs with conversion webhooks, you need to set up the hub-spoke conversion pattern.
Setting up conversion webhooks:
The conversion webhook is created for the hub version, with spoke versions specified using the --spoke flag:
kubebuilder create webhook --group batch --version v2 --kind CronJob --conversion --spoke v1
This command:
- Creates the conversion webhook infrastructure for version
v2(the hub) - Sets up conversion for version
v1(the spoke) to convert to/fromv2 - Generates
cronjob_conversion.gofiles with conversion method stubs
For multiple spokes, you can specify them as a comma-separated list:
kubebuilder create webhook --group batch --version v2 --kind CronJob --conversion --spoke v1,v1alpha1
This sets up v2 as the hub with both v1 and v1alpha1 as spokes.
What you need to implement:
The command generates method stubs that you’ll fill in during Step 4:
- Hub version: Implement
Hub()method (usually just a marker) - Spoke versions: Implement
ConvertTo(hub)andConvertFrom(hub)methods with your conversion logic
See the Multi-Version Tutorial for comprehensive guidance on implementing the conversion logic.
After scaffolding all webhooks, verify everything compiles:
make manifests
make generate
make build
Step 4: Port Your Custom Code
Now you need to manually port your custom business logic and configurations from the backup to the new project.
4.1 Port API definitions
Compare and merge your custom API fields and markers from your backup project.
Files to compare:
- Single-group:
api/v1/<kind>_types.go - Multi-group:
api/<group>/<version>/<kind>_types.go
What to port:
- Custom fields in Spec and Status structs
- Validation markers - e.g.,
+kubebuilder:validation:Minimum=0,+kubebuilder:validation:Pattern=... - CRD generation markers - e.g.,
+kubebuilder:printcolumn,+kubebuilder:resource:scope=Cluster - SubResources - e.g.,
+kubebuilder:subresource:status,+kubebuilder:subresource:scale - Documentation comments - Used for CRD descriptions
See CRD Generation, CRD Validation, and Markers for all available markers.
After porting API definitions, regenerate and verify:
make manifests # Generate CRD manifests from your types
make generate # Generate DeepCopy methods
This ensures your API types and CRD manifests are properly generated before moving forward.
4.2 Port controller logic
Files to compare:
- File location:
internal/controller/<kind>_controller.go(orcontrollers/in older versions)
What to port:
- Reconcile function implementation - Your core business logic
- Helper functions - Any additional functions in the controller file
- RBAC markers -
+kubebuilder:rbac:groups=...,resources=...,verbs=... - Additional watches - Custom watch configurations in
SetupWithManager - Imports - Any additional packages your controller needs
- Struct fields - Custom fields added to the Reconciler struct
See RBAC Markers for details on permission markers.
After porting controller logic, regenerate manifests and verify compilation:
make generate
make manifests
make build
4.3 Port webhook implementations
Webhooks have changed location between Kubebuilder versions. Be aware of the path differences:
Legacy webhook location (Kubebuilder v3 and earlier):
api/v1/<kind>_webhook.goapi/<group>/<version>/<kind>_webhook.go
Current webhook location (Kubebuilder v4+):
internal/webhook/v1/<kind>_webhook.gointernal/webhook/<version>/<kind>_webhook.go
What to port:
- Defaulting webhook -
Default()method implementation - Validation webhook -
ValidateCreate(),ValidateUpdate(),ValidateDelete()methods - Conversion webhook -
ConvertTo()andConvertFrom()methods (for multi-version APIs) - Helper functions - Any validation or defaulting helper functions
- Webhook markers - Usually auto-generated, but verify they match your needs
See Webhook Overview, Admission Webhook, and the Multi-Version Tutorial for details.
For conversion webhooks:
If you have conversion webhooks, ensure you used the create webhook --conversion --spoke <version> command in Step 3.4. This sets up the hub-spoke infrastructure automatically. You only need to fill in the conversion logic in the ConvertTo() and ConvertFrom() methods in your spoke versions, and the Hub() method in your hub version.
The command creates all the necessary boilerplate - you just implement the business logic for converting fields between versions.
After porting webhooks, regenerate and verify:
make generate
make manifests
make build
4.4 Configure Kustomize manifests
The config/ directory contains Kustomize manifests for deploying your operator. Compare with your backup to ensure all configurations are properly set up.
Review and update these directories:
-
config/default/kustomization.yaml- Main kustomization file- Ensure that webhook configurations is enabled if you have webhooks (
uncomment webhook-related patches) - Ensure that cert-manager is enabled if using webhooks (
uncomment certmanager resources) - Enable or disable metrics endpoint based on your original configuration
- Review namespace and name prefix settings
- Ensure that webhook configurations is enabled if you have webhooks (
-
config/manager/- Controller manager deployment- Usually no changes needed unless you have customizations, such as:
- If needed: compare resource limits and requests with your backup
- If needed: check environment variables
-
config/rbac/- RBAC configurations- Usually auto-generated from markers - no manual changes needed
- Only check if you have custom role bindings or service account configurations not covered by markers
-
config/webhook/- Webhook configurations (if applicable)- Usually auto-generated - no manual changes needed
- Only check if you have custom webhook service or certificate configurations
-
config/samples/- Sample CR manifests- Copy your sample resources from the backup
After configuring Kustomize, verify the manifests build correctly:
make all
make build-installer
4.5 Port main.go customizations (if any)
File: cmd/main.go
Most projects don’t need to customize main.go as Kubebuilder handles all the standard setup automatically
(registering APIs, setting up controllers and webhooks, manager initialization, metrics, etc.).
Only port customizations that are not part of the standard scaffold. Compare your backup main.go with the
new scaffolded one to identify any custom logic you added.
4.6 Port additional customizations
Now try building and testing the project to identify any missing pieces:
make all
If you encounter issues, you may need to port additional customizations from your backup:
Dependencies (go.mod):
- Compare your backup
go.modwith the new one - Add any additional dependencies not part of the standard scaffold:
go get <package-name>@<version> - Then tidy up:
go mod tidy
Compare your project structure with your backup to identify any other custom files or directories you may have added, such as:
- Compare the two Makefiles carefully using diff tools you may have custom targets: deployment helpers, code generation scripts, etc.
- Compare the Dockerfile you may have some custom build configurations:
- Ensure that you ported any testing-related files and configurations if you have tests in your project.
After porting all customizations, run the full build and test cycle:
make all
Step 5: Test and Deploy
Thoroughly test your migrated project to ensure everything works as expected.
Additional Resources
- Migration Overview - Overview of all migration options
- PROJECT File Reference - Understanding the PROJECT file
- What’s in a basic project? - Understanding project structure
- Alpha Generate Command - Automated re-scaffolding
- Alpha Update Command - Automated migration
- Using External Types - Controllers for types not defined in your project
- CRD Generation - Generating CRDs from Go types
- CRD Validation - Adding validation to your APIs
- Markers - All available markers for code generation
- RBAC Markers - Generating RBAC manifests
- Webhook Overview - Understanding webhooks
- Admission Webhook - Implementing admission webhooks
- Multi-Version Tutorial - Handling multiple API versions
- Deploying cert-manager - Required for webhooks
- Configuring EnvTest - Testing with EnvTest