Finalizers
Finalizers are a crucial mechanism in Kubernetes for ensuring proper cleanup of resources. They provide a way to guarantee that cleanup operations are completed before a resource is actually deleted from the cluster.
What are Finalizers?
Finalizers are markers on resources that prevent their deletion until certain conditions are met. They are used to:
- Ensure proper cleanup of dependent resources
 - Prevent accidental deletion of critical resources
 - Guarantee that cleanup operations are completed successfully
 
How Kubernetes Handles Finalizers
When a resource is marked for deletion:
- Kubernetes adds a 
deletionTimestampto the resource - The resource remains in the cluster until all finalizers are removed
 - Each finalizer must explicitly remove itself after completing its cleanup
 - Only when all finalizers are removed is the resource actually deleted
 
This mechanism ensures that cleanup operations are:
- Guaranteed to run
 - Run in a controlled manner
 - Completed before resource deletion
 
Implementing Finalizers
To implement a finalizer, create a class that implements IEntityFinalizer<TEntity>:
public class DemoEntityFinalizer(
    ILogger<DemoEntityFinalizer> logger,
    IKubernetesClient client) : IEntityFinalizer<V1DemoEntity>
{
    public async Task FinalizeAsync(V1DemoEntity entity, CancellationToken token)
    {
        logger.LogInformation("Finalizing entity {Entity}", entity);
        try
        {
            // Clean up resources
            await CleanupResources(entity);
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "Error finalizing entity {Entity}", entity);
            throw; // Re-throw to prevent finalizer removal
        }
    }
}
Using Finalizers
Finalizers are automatically attached to entities using the EntityFinalizerAttacher delegate. This delegate is injected into your controller and handles the finalizer attachment:
[EntityRbac(typeof(V1DemoEntity), Verbs = RbacVerb.All)]
public class V1DemoEntityController(
    ILogger<V1DemoEntityController> logger,
    EntityFinalizerAttacher<DemoEntityFinalizer, V1DemoEntity> finalizer)
    : IEntityController<V1DemoEntity>
{
    public async Task ReconcileAsync(V1DemoEntity entity, CancellationToken token)
    {
        // Attach the finalizer to the entity
        entity = await finalizer(entity, token);
        // Continue with reconciliation logic
        logger.LogInformation("Reconciling entity {Entity}", entity);
    }
    public async Task DeletedAsync(V1DemoEntity entity, CancellationToken token)
    {
        logger.LogInformation("Entity {Entity} was deleted", entity);
    }
}
Best Practices
1. Idempotency
- Make finalizer logic idempotent
 - Handle cases where resources are already deleted
 - Check resource existence before attempting cleanup
 
public async Task FinalizeAsync(V1DemoEntity entity, CancellationToken token)
{
    // Check if resources still exist before cleanup
    var resources = await GetResources(entity);
    if (!resources.Any())
    {
        // Resources already cleaned up
        return;
    }
    // Perform cleanup
    await CleanupResources(resources);
}
2. Error Handling
- Handle errors gracefully
 - Log errors with appropriate context
 - Consider implementing retry logic for transient failures
 
public async Task FinalizeAsync(V1DemoEntity entity, CancellationToken token)
{
    try
    {
        await FinalizeInternal(entity, token);
    }
    catch (Exception ex)
    {
        logger.LogError(ex, "Error finalizing entity {Entity}", entity);
        // Re-throw to prevent finalizer removal
        throw;
    }
}
3. Resource Management
- Clean up all resources created by the entity
 - Handle dependencies between resources
 - Consider cleanup order (e.g., delete pods before services)
 
Common Pitfalls
1. Stuck Resources
If a finalizer fails to complete:
- The resource will remain in the cluster
 - It will be marked for deletion but never actually deleted
 - Manual intervention may be required
 
To fix stuck resources:
- Identify the failing finalizer
 - Fix the underlying issue
 - Manually remove the finalizer only if necessary:
kubectl patch <resource> <name> -p '{"metadata":{"finalizers":[]}}' --type=merge 
Only remove finalizers manually as a last resort. This can lead to orphaned resources and inconsistent cluster state.
2. Race Conditions
- Multiple finalizers running concurrently
 - Resources being deleted by other controllers
 - Network issues during cleanup
 
Solution: Implement proper error handling and retry logic.
3. Infinite Loops
- Finalizer logic that never completes
 - Resources that can't be deleted
 - External dependencies that are unavailable
 
Solution: Implement timeouts and fallback mechanisms.
When to Use Finalizers
Use finalizers when:
- Resources need guaranteed cleanup
 - External systems need to be notified of deletion
 - Dependencies need to be cleaned up in a specific order
 - Resource deletion needs to be atomic
 
Don't use finalizers for:
- Simple logging or monitoring
 - Non-critical cleanup tasks
 - Operations that can be handled by the 
DeletedAsyncmethod in controllers