Skip to content

Patching Resources

PHP K8s supports both JSON Patch (RFC 6902) and JSON Merge Patch (RFC 7396) for precise updates to Kubernetes resources without replacing the entire object.

Why Use Patching?

Traditional update() replaces the entire resource, which can:

  • Overwrite concurrent changes
  • Require fetching the full resource first
  • Be inefficient for small changes

Patching allows:

  • ✅ Atomic, targeted updates
  • ✅ Concurrent-safe modifications
  • ✅ Efficient network usage
  • ✅ Server-side conflict resolution

JSON Patch (RFC 6902)

JSON Patch provides precise, surgical updates with validation capabilities.

Supported Operations

  • add - Add a value at a specific path
  • remove - Remove a value at a specific path
  • replace - Replace a value at a specific path
  • move - Move a value from one path to another
  • copy - Copy a value from one path to another
  • test - Test that a value at a path matches the expected value

Basic Usage

php
use RenokiCo\PhpK8s\Patches\JsonPatch;

$deployment = $cluster->getDeploymentByName('my-app', 'production');

// Create a JSON Patch
$patch = new JsonPatch();
$patch
    ->test('/metadata/name', 'my-app')           // Verify name
    ->replace('/spec/replicas', 5)               // Scale to 5
    ->add('/metadata/labels/version', 'v2.0')    // Add label
    ->remove('/metadata/labels/deprecated');     // Remove label

// Apply the patch
$deployment->jsonPatch($patch);

Array Format

You can also use arrays directly:

php
$patchArray = [
    ['op' => 'test', 'path' => '/spec/replicas', 'value' => 3],
    ['op' => 'replace', 'path' => '/spec/replicas', 'value' => 5],
    ['op' => 'add', 'path' => '/metadata/labels/updated', 'value' => 'true'],
];

$deployment->jsonPatch($patchArray);

Advanced Examples

Conditional Updates with Test

php
$patch = new JsonPatch();
$patch
    ->test('/spec/replicas', 3)        // Only proceed if currently 3
    ->replace('/spec/replicas', 5);     // Scale to 5

try {
    $deployment->jsonPatch($patch);
    echo "Scaled successfully";
} catch (\Exception $e) {
    echo "Precondition failed - replica count has changed";
}

Update Container Image

php
$patch = new JsonPatch();
$patch->replace(
    '/spec/template/spec/containers/0/image',
    'myapp:v2.0.0'
);

$deployment->jsonPatch($patch);

Add Environment Variable

php
$patch = new JsonPatch();
$patch->add(
    '/spec/template/spec/containers/0/env/-',  // -  adds to end of array
    ['name' => 'NEW_VAR', 'value' => 'new_value']
);

$deployment->jsonPatch($patch);

Move a Label

php
$patch = new JsonPatch();
$patch->move(
    '/metadata/labels/old-label',
    '/metadata/labels/new-label'
);

$deployment->jsonPatch($patch);

JSON Merge Patch (RFC 7396)

JSON Merge Patch provides a simpler, more intuitive way to update resources by merging changes.

Basic Usage

php
use RenokiCo\PhpK8s\Patches\JsonMergePatch;

$deployment = $cluster->getDeploymentByName('my-app', 'production');

// Create a JSON Merge Patch
$patch = new JsonMergePatch();
$patch
    ->set('spec.replicas', 5)
    ->set('metadata.labels.version', 'v2.0')
    ->remove('metadata.labels.deprecated');  // Sets to null

// Apply the patch
$deployment->jsonMergePatch($patch);

Array Format

php
$patchArray = [
    'spec' => [
        'replicas' => 5
    ],
    'metadata' => [
        'labels' => [
            'version' => 'v2.0',
            'deprecated' => null  // null removes the label
        ]
    ]
];

$deployment->jsonMergePatch($patchArray);

Advanced Examples

Update Multiple Fields

php
$patch = new JsonMergePatch();
$patch
    ->set('spec.replicas', 5)
    ->set('spec.template.spec.containers.0.image', 'myapp:v2.0.0')
    ->set('metadata.annotations.updated-at', date('c'));

$deployment->jsonMergePatch($patch);

Update Nested Objects

php
$patch = new JsonMergePatch([
    'spec' => [
        'template' => [
            'spec' => [
                'containers' => [
                    [
                        'name' => 'app',
                        'resources' => [
                            'limits' => [
                                'memory' => '1Gi',
                                'cpu' => '500m'
                            ]
                        ]
                    ]
                ]
            ]
        ]
    ]
]);

$deployment->jsonMergePatch($patch);

When to Use Which

Use JSON Patch When:

  • ✅ You need atomic operations with validation
  • ✅ You want to verify preconditions with test
  • ✅ You need precise array element manipulation
  • ✅ You want to move or copy values
  • ✅ Order of operations matters

Use JSON Merge Patch When:

  • ✅ You want simple, intuitive updates
  • ✅ You're updating multiple related fields
  • ✅ You don't need precondition testing
  • ✅ You prefer declarative syntax
  • ✅ You're merging configuration changes

Use Server Side Apply When:

  • ✅ Multiple controllers manage the same resource
  • ✅ You want automatic conflict detection
  • ✅ You need field ownership tracking
  • ✅ You're implementing a controller or operator
  • ✅ You want declarative, idempotent operations

See the Server Side Apply Guide for more details.

HTTP Content Types

The library automatically sets the correct Content-Type headers:

  • JSON Patch: application/json-patch+json
  • JSON Merge Patch: application/merge-patch+json

Practical Examples

Rolling Update with Version Check

php
$deployment = $cluster->getDeploymentByName('api');

// Only update if current version is v1.0
$patch = new JsonPatch();
$patch
    ->test('/metadata/labels/version', 'v1.0')
    ->replace('/spec/template/spec/containers/0/image', 'api:v1.1')
    ->replace('/metadata/labels/version', 'v1.1');

try {
    $deployment->jsonPatch($patch);
    echo "Rolling update initiated";
} catch (\Exception $e) {
    echo "Version mismatch - update aborted";
}

Add Sidecar Container

php
$sidecar = [
    'name' => 'logging',
    'image' => 'fluent/fluent-bit:latest',
    'volumeMounts' => [
        ['name' => 'logs', 'mountPath' => '/var/log']
    ]
];

$patch = new JsonPatch();
$patch->add('/spec/template/spec/containers/-', $sidecar);

$deployment->jsonPatch($patch);

Update Resource Limits

php
$patch = new JsonMergePatch([
    'spec' => [
        'template' => [
            'spec' => [
                'containers' => [
                    [
                        'name' => 'app',
                        'resources' => [
                            'requests' => [
                                'cpu' => '100m',
                                'memory' => '128Mi'
                            ],
                            'limits' => [
                                'cpu' => '500m',
                                'memory' => '512Mi'
                            ]
                        ]
                    ]
                ]
            ]
        ]
    ]
]);

$deployment->jsonMergePatch($patch);

Batch Label Updates

php
$pods = $cluster->getAllPods('production');

$patch = new JsonMergePatch([
    'metadata' => [
        'labels' => [
            'updated-at' => date('Y-m-d'),
            'managed-by' => 'php-k8s'
        ]
    ]
]);

foreach ($pods as $pod) {
    $pod->jsonMergePatch($patch);
}

Error Handling

php
use RenokiCo\PhpK8s\Exceptions\KubernetesAPIException;

try {
    $patch = new JsonPatch();
    $patch
        ->test('/spec/replicas', 3)
        ->replace('/spec/replicas', 5);

    $deployment->jsonPatch($patch);
} catch (KubernetesAPIException $e) {
    if ($e->getCode() === 422) {
        echo "Patch validation failed: {$e->getMessage()}";
    } else {
        echo "Patch failed: {$e->getMessage()}";
    }
}

Supported Resources

Both patch methods work with all Kubernetes resources that extend K8sResource:

  • Deployments, StatefulSets, DaemonSets
  • Pods, Jobs, CronJobs
  • Services, Ingresses
  • ConfigMaps, Secrets
  • PersistentVolumes, PersistentVolumeClaims
  • And all other standard Kubernetes resources

Best Practices

  1. Use JSON Patch for critical updates - The test operation prevents race conditions
  2. Use JSON Merge Patch for bulk changes - Simpler syntax for multiple related updates
  3. Handle errors gracefully - Wrap in try-catch blocks
  4. Validate paths - Ensure JSON paths are correct
  5. Understand array semantics - - appends to arrays, indices update specific elements
  6. Refresh after patching - Call ->refresh() to get updated state
  7. Test in development - Verify patches work before production use

Limitations

  • JSON Merge Patch cannot remove array elements individually
  • JSON Patch requires exact path knowledge
  • Some operations may fail due to server-side validation
  • Concurrent patches may conflict

Next Steps


Originally from renoki-co/php-k8s documentation, adapted for cuppett/php-k8s fork

Released under the Apache-2.0 License. Originally from renoki-co/php-k8s.