Helm has long been the standard for managing Kubernetes applications using packaged charts, bringing a level of reproducibility and automation to the deployment process. However, some operational tasks, such as renaming a release or migrating objects between charts, have traditionally required cumbersome workarounds. With the introduction of the --take-ownership flag in Helm v3.17 (released in January 2025), a long-standing pain point is finally addressed—at least partially.
The take-ownership feature represents the continuing evolution of Helm. Learn about this and other cutting-edge capabilities in our Helm Charts Package Management Guide
In this post, we will explore:
What the --take-ownership flag does
Why it was needed
The caveats and limitations
Real-world use cases where it helps
When not to use it
Understanding Helm Release Ownership and Object Management
When Helm installs or upgrades a chart, it injects metadata—labels and annotations—into every managed Kubernetes object. These include:
This metadata serves an important role: Helm uses it to track and manage resources associated with each release. As a safeguard, Helm does not allow another release to modify objects it does not own and when you trying that you will see messages like the one below:
Error: Unable to continue with install: Service "provisioner-agent" in namespace "test-my-ns" exists and cannot be imported into the current release: invalid ownership metadata; annotation validation error: key "meta.helm.sh/release-name" must equal "dp-core-infrastructure11": current value is "dp-core-infrastructure"
While this protects users from accidental overwrites, it creates limitations for advanced use cases.
Why --take-ownership Was Needed
Let’s say you want to:
Rename an existing Helm release from api-v1 to api.
Move a ConfigMap or Service from one chart to another.
Rebuild state during GitOps reconciliation when previous Helm metadata has drifted.
Previously, your only option was to:
Uninstall the existing release.
Reinstall under the new name.
This approach introduces downtime, and in production systems, that’s often not acceptable.
You’re refactoring a large chart into smaller, modular ones and need to reassign certain Service or Secret objects.
This flag allows the new release to take control of the object without deleting or recreating it.
✅ 3. GitOps Drift Reconciliation
If objects were deployed out-of-band or their metadata changed unintentionally, GitOps tooling using Helm can recover without manual intervention using --take-ownership.
Best Practices and Recommendations
Use this flag intentionally, and document where it’s applied.
If possible, remove the previous release after migration to avoid confusion.
Monitor Helm’s behavior closely when managing shared objects.
For non-Helm-managed resources, continue to use kubectl annotate or kubectl label to manually align metadata.
Conclusion
The --take-ownership flag is a welcomed addition to Helm’s CLI arsenal. While not a universal solution, it smooths over many of the rough edges developers and SREs face during release evolution and GitOps adoption.
It brings a subtle but powerful improvement—especially in complex environments where resource ownership isn’t static.
Stay updated with Helm releases, and consider this flag your new ally in advanced release engineering.
The Modern Way: helm upgrade –take-ownership
Since Helm 3.17, most of the manual adoption workflow below is one flag:
With --take-ownership, Helm skips the ownership check that normally fails with “invalid ownership metadata; annotation validation error” and rewrites the ownership metadata to point at the new release. The resources’ spec is untouched — only the Helm bookkeeping changes. Two caveats: it applies to every conflicting resource in the chart (there is no per-resource opt-in), and it will happily steal resources from another Helm release, not just from kubectl-created objects — so run it with --dry-run first in anything shared.
Manual Adoption: The Three Ownership Markers
On older Helm versions (or when you want per-resource control), adoption means setting the three markers Helm checks before managing a resource:
After those three, the next helm upgrade treats the resource as its own and reconciles it against the chart template. This is the route to migrate kubectl-managed or kustomize-managed infrastructure into a chart incrementally, one resource at a time, without deleting anything.
Frequently Asked Questions
What does the Helm u002du002dtake-ownership flag do?
The u003ccodeu003eu002du002dtake-ownershipu003c/codeu003e flag allows Helm to bypass ownership validation and claim control of Kubernetes resources that belong to another release. It updates the u003cstrongu003emeta.helm.sh/release-nameu003c/strongu003e annotation to associate objects with the current release, enabling zero-downtime release renames and chart migrations.
When should I use Helm take ownership?
Use u003ccodeu003eu002du002dtake-ownershipu003c/codeu003e when renaming releases without downtime, migrating objects between charts, or fixing GitOps drift. It’s ideal for u003cstrongu003eproduction environmentsu003c/strongu003e where uninstall/reinstall cycles aren’t acceptable. Always document usage and clean up previous releases afterward.
What are the limitations of Helm take ownership?
The flag u003cstrongu003edoesn’t clean upu003c/strongu003e references from previous releases or protect against future uninstalls of the original release. It only works with Helm-managed resources, not completely unmanaged Kubernetes objects. Manual cleanup of old releases is still required.
Is Helm take ownership safe for production use?
Yes, but use it u003cstrongu003eintentionally and carefullyu003c/strongu003e. The flag bypasses Helm’s safety checks, so ensure you understand the ownership implications. Test in staging first, document all usage, and monitor for conflicts. Remove old releases after successful migration to avoid confusion.
Which Helm version introduced the take ownership flag?
The u003ccodeu003eu002du002dtake-ownershipu003c/codeu003e flag was introduced in u003cstrongu003eHelm v3.17u003c/strongu003e, released in January 2025. This feature addresses long-standing pain points with release renaming and chart migrations that previously required downtime-inducing uninstall/reinstall cycles.
Kubernetes ConfigMaps are a powerful tool for managing configuration data separately from application code. However, they can sometimes lead to issues during deployment, particularly when a ConfigMap referenced in a Pod specification is missing, causing the application to fail to start. This is a common scenario that can lead to a CreateContainerConfigError and halt your deployment pipeline.
Understanding the Problem
When a ConfigMap is referenced in a Pod’s specification, Kubernetes expects the ConfigMap to be present. If it is not, Kubernetes will not start the Pod, leading to a failed deployment. This can be problematic in situations where certain configuration data is optional or environment-specific, such as proxy settings that are only necessary in certain environments.
Making ConfigMap Values Optional
Kubernetes provides a way to define ConfigMap items as optional, allowing your application to start even if the ConfigMap is not present. This can be particularly useful for environment variables that only need to be set under certain conditions.
Here’s a basic example of how to make a ConfigMap optional:
name: example-configmap refers to the ConfigMap that might or might not be present.
optional: true ensures that the Pod will still start even if example-configmap or the optional-key within it is missing.
Practical Use Case: Proxy Configuration
A common use case for optional ConfigMap values is setting environment variables for proxy configuration. In many enterprise environments, proxy settings are only required in certain deployment environments (e.g., staging, production) but not in others (e.g., local development).
In this setup, if the proxy-config ConfigMap is missing, the application will still start, simply without the proxy settings.
Sample Application
Let’s walk through a simple example to demonstrate this concept. We will create a deployment for an application that uses optional configuration values.
Deploy the application using kubectl apply -f <your-deployment-file>.yaml.
If the app-config ConfigMap is present, the Pod will output “Hello, World!”.
If the ConfigMap is missing, the Pod will start, but no greeting will be echoed.
Conclusion
Optional ConfigMap values are a simple yet effective way to make your Kubernetes deployments more resilient and adaptable to different environments. By marking ConfigMap keys as optional, you can prevent deployment failures and allow your applications to handle missing configuration gracefully.
KubeSec is another tool to help improve the security of our Kubernetes cluster. And we’re seeing so many agencies focus on security to highlight this topic’s importance in modern architectures and deployments. Security is a key component now, probably the most crucial. We need all to step up our game on that topic, and that’s why it is essential to have tools in our toolset to help us on that task without being fully security experts on each of the technologies, such as Kubernetes in this case.
KubeSec is an open-source tool developed by a cloud-native and open-source security consultancy named ControlPlane that helps us perform a security risk analysis on Kubernetes resources.
How Does KubeSec Work?
KubeSec works based on the Kubernetes Manifest Files you use to deploy the different resources, so you need to provide the YAML file to one of the running ways this tool supports. This is an important topic, “one of the running ways,” because KubeSec supports many different running modes that help us cover other use cases.
You can run KubeSec in the following ones:
HTTP Mode: KubeSec will be listening to HTTP requests with the content of the YAML and provide a report based on that. This is useful in cases needing server mode execution, such as CICD pipelines, or just security servers to be used by some teams, such as DevOps or Platform Engineering. Also, another critical use-case of this mode is to be part of a Kubernetes Admission Controller on your Kubernetes Cluster so that you can enforce this when developers are deploying resources into the platform itself.
SaaS Mode: Similar to HTTP mode but without needing to host it yourself, all available behind kubesec.io kubesec.io when the SaaS mode is of your preference, and you’re not managing sensitive information on those components.
CLI Mode: Just to run it yourself as part of your local tests, you will have available another CLI command here: kubesec scan k8s-deployment.yaml
Docker Mode: Similar to CLI mode but as part of a docker image, it can also be compatible with the CICD pipelines based on containerized workloads.
KubeScan Output Report
What you will get out of the execution if KubeScan of any of its forms is a JSON report that you can use to improve and score the security level of your Kubernetes resources and some ways to improve it. The reason behind using JSON as the output also simplifies the tool’s usage in automated workloads such as CICD pipelines. Here you can see a sample of the output report you will get:
The important thing about the output is the kind of information you will receive from it. As you can see in the picture above, it is separated into two different sections per object. The first one is the “score,” that are the implemented things related to security that provide some score for the security of the object. But also you will have an advice section that provides some things and configurations you can do to improve that score, and because of that, also the global security of the Kubernetes object itself.
Kubescan also leverages another tool we have commented not far enough on this site, Kubeconform, so you can also specify the target Kubernetes version you’re hitting to have a much more precise report of your specific Kubernetes Manifest. To do that, you can specify the argument --kubernetes-version when you’re launching the command, as you can see in the picture below:
How To Install KubeScan?
Installation also provides different ways and flavors to see what is best for you. Here are some of the options available at the moment for writing this article:
Emphasizing the paramount importance of security in today’s intricate architectures, KubeSec emerges as a vital asset for bolstering the protection of Kubernetes clusters. Developed by ControlPlane, this open-source tool facilitates comprehensive security risk assessments of Kubernetes resources. Offering versatility through multiple operational modes—such as HTTP, SaaS, CLI, and Docker—KubeSec provides tailored support for diverse scenarios. Its JSON-based output streamlines integration into automated workflows, while its synergy with Kubeconform ensures precise analysis of Kubernetes Manifests. KubeSec’s user-friendly approach empowers security experts and novices, catalyzing an elevated standard of Kubernetes security across the board.
This article will cover how to enhance the security of your TIBCO BWCE images by creating a ReadOnlyFileSystem Image for TIBCO BWCE. In previous articles, we have commented on the benefits that this kind of image provides several advantages in terms of security, focusing on aspects such as reducing the attack surface by limiting the kind of things any user can do, even if they gain access to running containers.
This article is part of my comprehensive TIBCO Integration Platform Guide where you can find more patterns and best practices for TIBCO integration platforms.
The same applies in case any malware your image can have will have limited the possible actions they can do without any write access to most of the container.
How ReadOnlyFileSystem affects a TIBCO BWCE image?
This has a clear impact as the TIBCO BWCE image is an image that needs to write in several folders as part of the expected behavior of the application. That’s mandatory and non-dependent on the scripts you used to build your image.
As you probably know, TIBCO BWCE ships two sets of scripts to build the Docker base image: the main ones and the ones included in the folder reducedStartupTime, as you can see in the GitHub page but also inside your docker folder in the TIBCO-HOME after the installation as you can see in the picture below.
The main difference between them is where the unzip of the bwce-runtime is made. In the case of the default script, the unzip is done in the startup process of the image, and in the reducedStartupTime this is done in the building of the image itself. So, you can start thinking that the default ones need some writing access as they need to unzip the file inside the container, and that’s true.
But also, the reduced startupTime requires writing access to run the application; several activities are done regarding unzipping the EAR file, managing the properties file, and additional internal activities. So, no matter what kind of scripts you’re using, you must provide a write-access folder to do this activity.
By default, all these activities are limited to a single folder. If you keep everything by default, this is the /tmp folder, so you must provide a volume for that folder.
How to deploy a TIBCO BWCE application with the
Now, that is clear that you need a volume for the /tmp folder, and now you need to define the kind of volume that you want to use for this one. As you know, there are several kinds of volumes that you can determine depending on the requirements that you have.
In this case, the only requirement is to write access, but there is no need regarding storage and persistency, so, in that case, we can use an emptyDir mode. emptyDir content, which is erased when a pod is removed, is similar to the default behavior but allows writing permission on its content.
To show how the YAML would like, we will use the default one that we have available in the documentation here:
apiVersion: v1
kind: Pod
metadata:
name: bookstore-demo
labels:
app: bookstore-demo
spec:
containers:
- name: bookstore-demo
image: bookstore-demo:2.4.4
imagePullPolicy: Never
envFrom:
- configMapRef:
name: name
So, we will change that to include the volume, as you can see here:
Include the volumes section with a single volume definition with the name of tmp with an emptyDirdefinition.
Include a volumeMountssection for the tmpvolume that is mounted in the /tmp path to allow to write on that specific path to enable also the unzip of the bwce-runtime as well as all the additional activities that are required.
To trigger this behavior, include the readOnlyRootFilesystem flag in the securityContext section.
Conclusion
Incorporating a ReadOnlyFileSystem approach into your TIBCO BWCE images is a proactive strategy to fortify your application’s security posture. By curbing unnecessary write access and minimizing the potential avenues for unauthorized actions, you’re taking a vital step towards safeguarding your containerized environment.
This guide has unveiled the critical aspects of implementing such a security-enhancing measure, walking you through the process with clear instructions and practical examples. With a focus on reducing attack vectors and bolstering isolation, you can confidently deploy your TIBCO BWCE applications, knowing that you’ve fortified their runtime environment against potential threats.
One such important security feature is the use of ReadOnlyRootFilesystem, a powerful tool that can significantly enhance the security posture of your containers.
In the rapidly evolving software development and deployment landscape, containers have emerged as a revolutionary technology. Offering portability, efficiency, and scalability, containers have become the go-to solution for packaging and delivering applications. However, with these benefits come specific security challenges that must be addressed to ensure the integrity of your containerized applications.
A ReadOnlyRootFilesystem is precisely what it sounds like a filesystem that can only be read from, not written to. In containerization, the contents of a container’s filesystem are locked in a read-only state, preventing any modifications or alterations during runtime.
Advantages of Using ReadOnlyRootFilesystem
Reduced Attack Surface: One of the fundamental principles of cybersecurity is reducing the attack surface – the potential points of entry for malicious actors. Enforcing a ReadOnlyRootFilesystem eliminates the possibility of an attacker gaining write access to your container. This simple yet effective measure significantly limits their ability to inject malicious code, tamper with critical files, or install malware.
Immutable Infrastructure: Immutable infrastructure is a concept where components are never changed once deployed. This approach ensures consistency and repeatability, as any changes are made by deploying a new instance rather than modifying an existing one. By applying a ReadOnlyRootFilesystem, you’re essentially embracing the principles of immutable infrastructure within your containers, making them more resistant to unauthorized modifications.
Malware Mitigation: Malware often relies on gaining written access to a system to carry out its malicious activities. By employing a ReadOnlyRootFilesystem, you erect a significant barrier against malware attempting to establish persistence or exfiltrate sensitive data. Even if an attacker manages to compromise a container, their ability to install and execute malicious code is severely restricted.
Enhanced Forensics and Auditing: In the unfortunate event of a security breach, having a ReadOnlyRootFilesystem in place can assist in forensic analysis. Since the filesystem remains unaltered, investigators can more accurately trace the attack vector, determine the extent of the breach, and identify the vulnerable entry points.
Implementation Considerations
Implementing a ReadOnlyRootFilesystem in your containerized applications requires a deliberate approach:
Image Design: Build your container images with the ReadOnlyRootFilesystem concept in mind. Make sure to separate read-only and writable areas of the filesystem. This might involve creating volumes for writable data or using environment variables to customize runtime behavior.
Runtime Configuration: Containers often require write access for temporary files, logs, or other runtime necessities. Carefully design your application to use designated directories or volumes for these purposes while keeping the critical components read-only.
Testing and Validation: Thoroughly test your containerized application with the ReadOnlyRootFilesystem configuration to ensure it functions as intended. Pay attention to any runtime errors, permission issues, or unexpected behavior that may arise.
How to Define a Pod to be ReadOnlyRootFilesystem?
To define a Pod as “ReadOnlyRootFilesystem,” this is one of the flags that belong to the securityContext section of the pod, as you can see in the sample below:
As the adoption of containers continues to surge, so does the importance of robust security measures. Incorporating a ReadOnlyRootFilesystem into your container strategy is a proactive step towards safeguarding your applications and data. By reducing the attack surface, fortifying against malware, and enabling better forensics, you’re enhancing the overall security posture of your containerized environment.
As you embrace immutable infrastructure within your containers, you’ll be better prepared to face the ever-evolving landscape of cybersecurity threats. Remember, when it comes to container security, a ReadOnlyRootFilesystem can be the shield that protects your digital assets from potential harm.
In the dynamic and ever-evolving world of container orchestration, Kubernetes continues to reign as the ultimate choice for managing, deploying, and scaling containerized applications. As Kubernetes evolves, so do its features and capabilities, and one such fascinating addition is the concept of Ephemeral Containers. In this blog post, we will delve into the world of Ephemeral Containers, understanding what they are, exploring their primary use cases, and learning how to implement them, all with guidance from the Kubernetes official documentation.
What Are Ephemeral Containers?
Ephemeral Containers, introduced as an alpha feature in Kubernetes 1.16 and reached a stable level on the Kubernetes 1.25 version, offer a powerful toolset for debugging, diagnosing, and troubleshooting issues within your Kubernetes pods without requiring you to alter your pod’s original configuration. Unlike regular containers that are part of the central pod’s definition, ephemeral containers are dynamically added to a running pod for a short-lived duration, providing you with a temporary environment to execute diagnostic tasks.
The good thing about ephemeral containers is that they allow you to have all the required tools to do the job (debug, data recovery, or anything else that could be required) without adding more devices to the base containers and increasing the security risk based on that action.
Main Use-Cases of Ephemeral Containers
Troubleshooting and Debugging: Ephemeral Containers shine brightest when troubleshooting and debugging. They allow you to inject a new container into a problematic pod to gather logs, examine files, run commands, or even install diagnostic tools on the fly. This is particularly valuable when encountering issues that are difficult to reproduce or diagnose in a static environment.
Log Collection and Analysis: When a pod encounters issues, inspecting its logs is often essential. Ephemeral Containers make this process seamless by enabling you to spin up a temporary container with log analysis tools, giving you instant access to log files and aiding in identifying the root cause of problems.
Data Recovery and Repair: Ephemeral Containers can also be used for data recovery and repair scenarios. Imagine a situation where a database pod faces corruption. With an ephemeral container, you can mount the pod’s storage volume, perform data recovery operations, and potentially repair the data without compromising the running pod.
Resource Monitoring and Analysis: Performance bottlenecks or resource constraints can sometimes affect a pod’s functionality. Ephemeral Containers allow you to analyze resource utilization, run diagnostics, and profile the pod’s environment, helping you optimize its performance.
Implementing Ephemeral Containers
Thanks to Kubernetes ‘ user-friendly approach, implementing ephemeral containers is straightforward. Kubernetes provides the kubectl debug command, which streamlines the process of attaching ephemeral containers to pods. This command allows you to specify the pod and namespace and even choose the debugging container image to be injected.
You can go even beyond, and instead of adding the ephemeral containers to the running pod, you can do the same to a copy of the pod, as you can see in the following command:
Finally, once you have done your duty, you can permanently remove it using a kubectl delete command, and that’s it.
It’s essential to notice that all these actions require direct access to the environment. Even that temporarily generates a mismatch on the “infrastructure-as-code” deployment, as we’re manipulating the runtime status temporarily. Hence, this approach is much more challenging to implement if you use some GitOps practices or tools such as Rancher Fleet or ArgoCD.
Conclusion
Ephemeral Containers, while currently a stable feature since the Kubernetes 1.25 release, offer impressive capabilities for debugging and diagnosing issues within your Kubernetes pods. By allowing you to inject temporary containers into running pods dynamically, they empower you to troubleshoot problems, collect logs, recover data, and optimize performance without disrupting your application’s core functionality. As Kubernetes continues to evolve, adding features like Ephemeral Containers demonstrates its commitment to providing developers with tools to simplify the management and maintenance of containerized applications. So, the next time you encounter a stubborn issue within your Kubernetes environment, remember that Ephemeral Containers might be the debugging superhero you need!
Istio is a popular open-source service mesh that provides a range of powerful features for managing and securing microservices-based architectures. We have talked a lot about its capabilities and components, but today we will talk about how we can use Istio to help with the DNS resolution mechanism.
As you already know, In a typical Istio deployment, each service is accompanied by a sidecar proxy, Envoy, which intercepts and manages the traffic between services. The Proxy DNS capability of Istio leverages this proxy to handle DNS resolution requests more intelligently and efficiently.
Traditionally, when a service within a microservices architecture needs to communicate with another service, it relies on DNS resolution to discover the IP address of the target service. However, traditional DNS resolution can be challenging to manage in complex and dynamic environments, such as those found in Kubernetes clusters. This is where the Proxy DNS capability of Istio comes into play.
Istio Proxy DNS Capabilities
With Proxy DNS, Istio intercepts and controls DNS resolution requests from services and performs the resolution on their behalf. Instead of relying on external DNS servers, the sidecar proxies handle the DNS resolution within the service mesh. This enables Istio to provide several valuable benefits:
Service discovery and load balancing: Istio’s Proxy DNS allows for more advanced service discovery mechanisms. It can dynamically discover services and their corresponding IP addresses within the mesh and perform load balancing across instances of a particular service. This eliminates the need for individual services to manage DNS resolution and load balancing.
Security and observability: Istio gains visibility into the traffic between services by handling DNS resolution within the mesh. It can apply security policies, such as access control and traffic encryption, at the DNS level. Additionally, Istio can collect DNS-related telemetry data for monitoring and observability, providing insights into service-to-service communication patterns.
Traffic management and control: Proxy DNS enables Istio to implement advanced traffic management features, such as routing rules and fault injection, at the DNS resolution level. This allows for sophisticated traffic control mechanisms within the service mesh, enabling A/B testing, canary deployments, circuit breaking, and other traffic management strategies.
Istio Proxy DNS Use-Cases
There are some moments when you cannot or don’t want to rely on the normal DNS resolution. Why is that? Starting because DNS is a great protocol but lacks some capabilities, such as location discovery. If you have the same DNS assigned to three IPs, it will provide each of them in a round-robin fashion and cannot rely on the location.
Or you have several IPs, and you want to block some of them for some specific service; these are great things you can do with Istio Proxy DNS.
Istio Proxy DNS Enablement
You need to know that Istio Proxy DNS capabilities are not enabled by default, so you must help if you want to use it. The good thing is that you can allow that at different levels, from the full mesh level to just a single pod level, so you can choose what is best for you in each case.
For example, if we want to enable it at the pod level, we need to inject the following configuration in the Istio proxy:
The same configuration can be part of the Mesh level as part of the operator installation, as you can find the documentation here on the Istio official page.
Conclusion
In summary, the Proxy DNS capability of Istio enhances the DNS resolution mechanism within the service mesh environment, providing advanced service discovery, load balancing, security, observability, and traffic management features. Istio centralizes and controls DNS resolution by leveraging the sidecar proxies, simplifying the management and optimization of service-to-service communication in complex microservices architectures.
A proxy setting that makes the Istio sidecar intercept DNS queries from the workload and answer them from its own local cache, instead of forwarding everything to kube-dns. It reduces DNS load, gives consistent answers for ServiceEntry hosts, and enables resolution for VMs in the mesh.
When should I enable DNS proxying in Istio?
Three main triggers: heavy east-west traffic overwhelming kube-dns/CoreDNS; ServiceEntries with resolution: DNS whose hosts must resolve identically in every pod; and mesh-expansion scenarios with VMs that have no cluster DNS. If none apply, the default path is fine.
How do I verify the sidecar is actually answering DNS?
Check the proxy config: istioctl proxy-config dns <pod> lists the names the sidecar serves. At runtime, nslookup from the workload container answered with the sidecar’s address (typically the localhost DNS listener) confirms capture is active.
Does Istio DNS capture affect external (non-mesh) domains?
Captured queries for unknown names are forwarded upstream to the original resolvers, so external resolution keeps working. The cache only authoritatively answers for services and ServiceEntry hosts the proxy knows about.
A Helm subchart is simply a chart that lives inside another chart. That one sentence hides most of the questions people actually have when they start using them: where the subchart comes from, how its values get set, what it can and cannot see from the parent, how to turn it off, and how to deploy the same subchart twice with different configuration.
This guide covers all of it, starting from the basics and ending with the pattern this post was originally written about: running multiple instances of the same subchart with alias. Everything here applies to charts with apiVersion: v2, which is what Helm 3 and Helm 4 use. For the wider picture of how charts, repositories and releases fit together, start with the complete Helm charts guide.
What Is a Helm Subchart?
A subchart is a complete, standalone chart that another chart (the parent, sometimes called an umbrella chart) includes and renders as part of the same release. When you run helm install on the parent, Helm renders the parent’s templates and every enabled subchart’s templates together, and all resulting objects belong to a single release with a single revision history.
There are two ways a chart can become a subchart.
Declared in Chart.yaml (the normal way)
You list it in the dependencies section of the parent’s Chart.yaml, and Helm downloads it into the charts/ directory for you:
Any chart placed in the parent’s charts/ directory, either unpacked as a folder or as a .tgz archive, is treated as a dependency even if Chart.yaml does not mention it. Directory and file names starting with _ or . are ignored by the loader. This is handy for vendoring a chart you forked, but you lose version constraints and the lock file, so it is best reserved for special cases.
Subchart vs dependency: is there a difference?
In practice, no. “Dependency” is the declaration in Chart.yaml, “subchart” is the chart that ends up in charts/ and gets rendered. Every dependency becomes a subchart once it is fetched, and every subchart is a dependency of its parent. The distinction people usually mean when they search for “helm subchart vs dependency” is actually declared dependency vs manually vendored chart, which is the comparison above. If you want the full reference of every field in the dependencies block and how version ranges are resolved, see the Helm dependencies guide and the post on Helm version constraints.
helm dependency update, build and Chart.lock
Declaring a dependency does nothing on its own. You have to fetch it:
helm dependency update ./shop
update resolves each version range against the repository, downloads the matching archives into charts/, and writes Chart.lock with the exact versions and a digest of the dependency list. Run it whenever you change dependencies in Chart.yaml.
helm dependency build ./shop
build does the reproducible thing: it rebuilds charts/ from the versions pinned in Chart.lock without re-resolving ranges. This is what you want in CI.
Two errors you will see sooner or later:
found in Chart.yaml, but missing in charts/ directory means you declared a dependency and never fetched it. Run helm dependency build (or update).
the lock file (Chart.lock) is out of sync with the dependencies file (Chart.yaml) means someone edited Chart.yaml without updating the lock. Run helm dependency update and commit the new Chart.lock.
Commit Chart.lock. Whether to commit the charts/*.tgz files is a team choice: committing them makes builds hermetic, not committing them keeps the repository small and relies on helm dependency build in the pipeline.
Passing Values to a Subchart
This is the part that confuses people the most, and it follows one rule: values for a subchart go under a top-level key named after the subchart (or after its alias, as we will see later).
# shop/values.yaml
replicaCount: 3 # parent value
postgresql: # everything under this key goes to the postgresql subchart
auth:
database: shop
username: shop
primary:
persistence:
size: 20Gi
redis:
architecture: standalone
Inside the postgresql subchart, templates see .Values.auth.database, not .Values.postgresql.auth.database. Helm strips the prefix and hands the subchart only its own slice. Values you set in the parent under that key are merged on top of the subchart’s own values.yaml defaults, so you only need to write what you want to change.
To remove a default the subchart ships with, rather than overriding it, set it to null in the parent. The details and the edge cases of that are covered in Helm null values.
The parent can read subchart values, not the other way around
The parent’s templates can reference .Values.postgresql.auth.database because that key exists in the parent’s value tree. The subchart cannot reference replicaCount from the parent: it only receives its own section plus global. This is intentional. A subchart must render the same way whether it is installed alone or embedded, so it cannot depend on anything its parent happens to define.
Global Values
global is the one channel that reaches every chart in the tree:
Every chart, parent and subcharts, can read .Values.global.imageRegistry. Globals flow downwards and parent globals take precedence over any global a subchart defines in its own values.yaml.
Two practical warnings. First, a global only does something if the subchart’s templates actually read it. Bitnami charts, for example, honour global.imageRegistry and global.storageClass because their templates were written to; a random chart may ignore both. Check the subchart’s templates or README before relying on a global. Second, do not use global as a general-purpose way to share configuration between your own charts. It works, but it creates an invisible contract between charts that is hard to trace months later. Explicit per-subchart values are easier to review.
Enabling and Disabling Subcharts: condition and tags
Every declared dependency is rendered by default. To make one optional you add a condition, a tags list, or both.
The resolution rules, from the Helm documentation:
A condition is a YAML path into the parent’s values that must resolve to a boolean. You can list several comma-separated paths; the first one that exists wins and the rest are ignored.
tags enable a chart if any of its tags is true.
When both are present and the condition path is set in values, the condition always overrides tags.
If neither resolves, the chart is enabled.
The usual pattern is a condition per subchart for fine control and a shared tag for groups that should switch on and off together, such as everything related to monitoring:
A common mistake is to write condition: enabled instead of condition: postgresql.enabled. The path is resolved from the parent’s root, not from inside the subchart’s section.
import-values: Pulling Subchart Values Up to the Parent
Values normally flow down. import-values lets the parent pull specific values up from a subchart, which is useful when the subchart knows something the parent’s templates need, such as a port or a service name.
The exports format
The subchart publishes values under a top-level exports key:
# Chart.yaml (parent)
dependencies:
- name: api
version: "2.x.x"
repository: "https://example.com/charts"
import-values:
- data
After rendering, the parent sees .Values.servicePort at its root. The contents of exports.data are merged into the parent’s root values, not under a data key.
The child-parent format
When the subchart does not export anything, you can map any path explicitly:
dependencies:
- name: api
version: "2.x.x"
repository: "https://example.com/charts"
import-values:
- child: service
parent: apiService
Now .Values.apiService.port in the parent holds whatever the subchart had in service.port. Imported values override defaults in the parent’s values.yaml, but values passed at install time with -f or --set still win.
Use import-values sparingly. If the parent needs a value that the subchart also needs, it is often clearer to define it once in the parent and pass it down, or put it in global.
What a Subchart Cannot Do
Knowing the limits saves a lot of debugging time:
It cannot see parent values. Only its own section and global. There is no .Parent.Values.
It cannot override parent values. Values flow down; import-values is the only way up, and the parent opts into it.
Named templates are global. Everything defined with {{ define }} across the parent and all subcharts shares one namespace. If two charts define mychart.labels, one silently wins. That is why well-written charts prefix every template with the chart name, and why helm create generates names like {{ include "shop.fullname" . }}.
It is part of the same release. You cannot upgrade, roll back or uninstall a subchart independently. A rollback of the parent rolls back every subchart.
Hooks run in the same release. Subchart hooks execute alongside the parent’s, ordered by weight, which can surprise you if two charts both ship a pre-install migration job. See the Helm hooks guide for ordering rules.
Multiple Instances of the Same Subchart with alias
Now to the less common but very useful case: you want the same subchart twice, each copy with its own configuration.
When you need it
The scenario that led me to this pattern was a set of microservices that belong to one application and share the same technology base. All of them could use the same generic chart (in my case a TIBCO BusinessWorks Container Edition chart; it could just as well be a generic go-microservice or spring-boot chart), but each one needs:
its own image,
its own configuration and environment variables,
its own endpoints, often talking to different databases or external systems.
Without subcharts you end up copying the chart once per service, and every fix to the base chart has to be applied N times. With one subchart instantiated N times, the base chart is versioned once and each service is just a block of values.
Other real cases: two Redis instances (one cache, one queue) with different persistence settings, or a primary and a reporting database from the same PostgreSQL chart.
Declaring the instances
Start from a parent that includes the generic chart once:
# Chart.yaml
apiVersion: v2
name: orders-platform
description: Orders application deployed from a shared service chart
version: 0.2.0
appVersion: "2.7.2"
dependencies:
- name: service
version: "~0.2.0"
repository: "https://charts.example.com"
To have two instances, declare the same chart twice and give each an alias:
# Chart.yaml
apiVersion: v2
name: orders-platform
description: Orders application deployed from a shared service chart
version: 0.3.0
appVersion: "2.7.2"
dependencies:
- name: service
alias: order-api
version: "~0.2.0"
repository: "https://charts.example.com"
condition: order-api.enabled
- name: service
alias: order-worker
version: "~0.2.0"
repository: "https://charts.example.com"
condition: order-worker.enabled
name stays the same, so helm dependency update downloads the chart once. The alias is what makes each copy distinct: it becomes the values key for that instance and it becomes .Chart.Name inside the instance’s templates.
Anything you do not set falls back to the shared chart’s defaults, so each block contains only what makes that service different.
Three things that bite with aliases
Use lowercase, DNS-safe aliases. Because the alias replaces .Chart.Name, the standard fullname helper builds resource names from it. An alias like serviceA (which the first version of this post used) produces names such as myrelease-serviceA, which Kubernetes rejects because object names must be lowercase. Stick to order-api style names.
Hyphenated aliases need index in parent templates. If the parent’s own templates need to read an instance’s values, .Values.order-api.service.port is a template parse error. Use {{ index .Values "order-api" "service" "port" }} instead. Templates inside the subchart are unaffected, because they see their own slice without the prefix.
The shared chart must use its chart name in resource names. If the base chart hard-codes name: service in its Deployment, both instances render an object with the same name and the install fails. A chart designed for reuse builds names from include "service.fullname", which derives from .Chart.Name and therefore from the alias.
Library Charts vs Subcharts
A library chart (type: library in Chart.yaml) is also declared as a dependency, but it renders nothing by itself. It only provides named templates the parent can include.
Subchart (application)
Library chart
Renders Kubernetes objects
Yes
No
Has its own values.yaml section
Yes
No, uses the caller’s context
Can be installed alone
Yes
No
Typical use
Bundling a database, cache or service
Shared labels, Deployment skeletons, helpers
Multiple instances
With alias
Not needed: call the template N times
For the multi-microservice case above there is a real alternative: a library chart that defines a full Deployment/Service template, and a parent that calls it once per service from a range over values. That keeps every service in the parent’s own templates and avoids alias quirks, at the cost of writing a bit more template code. Aliased subcharts are simpler when the shared chart already exists as an installable chart; library charts are cleaner when you design the abstraction from scratch.
helm upgrade with Subcharts
Upgrading a release that contains subcharts is the same helm upgrade, with two details worth knowing.
If you bump a dependency version, you must refresh charts/ before upgrading, either explicitly or with the flag:
helm dependency update ./shop
helm upgrade shop ./shop -f values-prod.yaml
# or in one step: fetches dependencies if they are missing
helm upgrade shop ./shop -f values-prod.yaml --dependency-update
Note that --dependency-update only fetches dependencies that are missing; it does not replace an older archive that is already sitting in charts/. After changing a version range, run helm dependency update explicitly.
Values behave as in any upgrade. Without --reuse-values, Helm starts from the chart’s defaults plus whatever you pass on this command; that is usually what you want, because a new subchart version may have renamed or added keys. --reuse-values merges your overrides on top of the previous release’s values and can hide new subchart defaults. --reset-then-reuse-values is the middle ground: chart defaults first, then the last release’s values, then your overrides. Before a subchart major version bump, render both versions with helm template and diff them. The advanced Helm commands post has more on inspecting a release before touching it.
Umbrella Charts: When to Use Them and When to Avoid Them
An umbrella chart is a parent whose main job is to bundle several subcharts into one deployable unit. Subcharts make them easy to build, which is exactly why they get overused.
They work well when:
the components share a lifecycle and are always released together, such as an application and its dedicated cache,
you want one versioned artifact per environment that says exactly what is deployed,
the same generic chart is instantiated several times, as in the alias example.
They hurt when:
components have different release cadences, because every change to one service creates a new revision of all of them and a rollback reverts everything,
different teams own different components and step on each other’s values,
the release grows so large that a single failed hook or a single invalid object blocks the whole upgrade.
If you are deciding how to organise charts across many services, the trade-offs between one central chart and per-service charts are covered in detail in Helm chart management: centralized vs per-service. Whatever structure you choose, test the charts with the subcharts enabled, and validate the values each instance receives with a values schema.
Frequently Asked Questions
What is a subchart in Helm?
A subchart is a complete Helm chart included inside another chart, either declared in the parent’s Chart.yaml under dependencies and fetched into charts/, or placed there manually. It is rendered as part of the parent’s release, receives its values from a key named after it in the parent’s values, and cannot see the parent’s own values except for global.
What is the difference between a Helm subchart and a dependency?
They are two views of the same thing. A dependency is the entry in Chart.yaml that says which chart and version to use; the subchart is that chart once it has been downloaded into charts/ and rendered. The only practical distinction is between declared dependencies, which have version ranges and a Chart.lock, and charts copied manually into charts/, which have neither.
How do I pass values to a Helm subchart?
Put them under a top-level key with the subchart’s name, or its alias if it has one, in the parent’s values.yaml, or use --set subchartname.key=value. Helm strips that prefix and merges the section over the subchart’s own defaults, so inside the subchart the values appear at .Values.key. Values that every chart should see go under global.
Can a subchart access the parent chart’s values?
No. A subchart only receives its own section of values and the global section. This keeps subcharts self-contained so they render identically whether installed alone or embedded. If a subchart needs something from the parent, pass it explicitly under the subchart’s key or place it in global.
How do I deploy the same Helm chart multiple times as subcharts?
Declare the same chart several times in dependencies with the same name and a different alias for each entry. Each alias becomes its own values key and its own .Chart.Name, so every instance gets separate configuration and separate resource names. Use lowercase, DNS-safe aliases, and make sure the shared chart builds resource names from its fullname helper rather than hard-coding them.
How do I disable a subchart?
Add condition: <subchart>.enabled to the dependency in Chart.yaml and set <subchart>.enabled: false in values or with --set. Alternatively assign tags and set tags.<tag>: false. If both are defined, a condition that resolves in values always overrides tags.
Do I need to run helm dependency update before helm upgrade?
Yes, whenever you change a dependency version in Chart.yaml. helm upgrade --dependency-update fetches dependencies that are missing from charts/, but it does not replace an archive that is already there, so after bumping a version run helm dependency update and commit the new Chart.lock. In CI, helm dependency build recreates charts/ from the lock file.
What is the difference between a library chart and a subchart?
A subchart is an application chart that renders its own Kubernetes objects and has its own values section. A library chart, marked with type: library, renders nothing and only provides named templates for the parent to include. Use subcharts to bundle components, and library charts to share template code across many charts.
Conclusion
Subcharts are Helm’s composition mechanism, and most of their behaviour follows from two ideas: values flow down under a key named after the subchart, and everything renders into one release. Once that is clear, global, condition, tags and import-values are just controlled exceptions to the flow.
The multi-instance pattern with alias builds on the same rules. It lets you keep one well-tested generic chart and instantiate it for every service that shares the same base, each with its own values, instead of maintaining a copy per service. Use it when the instances genuinely share a lifecycle, keep aliases lowercase, and reach for per-service releases or a library chart when they do not. That keeps the benefits of reuse without turning your umbrella chart into the monolith containers were supposed to get rid of.
Kubernetes API changes quite a lot, and we know that in every new version, they are adding new capabilities at the same time that they are deprecating the old ones, so it is a constant evolution, as we already stated in previous articles, as you can see, here regarding Autoscaling v2 and Vertical Autoscaling.
Some of these changes are related to the shift in the apiVersion of some objects, and you have probably already suffered from that v1/alpha going to v1/beta or just moving to a final v1 and deprecating the previous one. So, in the end, it is crucial to ensure that your manifest is in sync with the target version you’re deploying, and some tools can help us with that, including Kubeconform.
What is Kubeconform?
Kubeconform is a powerful utility designed to assist in Kubernetes configuration management and validation. As Kubernetes continues to gain popularity as the go-to container orchestration platform, ensuring the correctness and consistency of configuration files becomes crucial. Kubeconform addresses this need by providing a comprehensive toolset to validate Kubernetes configuration files against predefined standards or custom rules.
Kubeconform supports multiple versions of Kubernetes, allowing you to validate configuration files against different API versions. This flexibility is beneficial when working with clusters running different Kubernetes versions or migrating applications across sets with varying configurations.
Another great feature of Kubeconform is its ability to enforce best practices and standards across Kubernetes configurations. It allows you to define rules, such as enforcing proper labels, resource limits, or security policies, and then validates your configuration files against these rules. This helps catch potential issues early on and ensures that your deployments comply with established guidelines.
How to install Kubeconform?
Kubeconform can be installed from different sources, the most usual ones the standard for your environment using package managers such as brew, apt or similar ones or just getting the binaries from its GitHub page: https://github.com/yannh/kubeconform/releases.
How to launch Kubeconform from the Command Line?
Kubeconform is shipped as a small binary targeted to be executed in the CLI interface and tries to keep its interface minimal to ensure compatibility. Hence, it receives an argument with the file or folder with the manifest files that you want to check, as you can see here:
Then you have several options to do other things, such as the ones shown below:
-ignore-filename-pattern value
regular expression specifying paths to ignore (can be specified multiple times)
-ignore-missing-schemas
skip files with missing schemas instead of failing
-Kubernetes-version string
version of Kubernetes to validate against, e.g.: 1.18.0 (default “master”)
-output string
output format – json, junit, pretty, tap, text (default “text”)
-reject string
comma-separated list of kinds or GVKs to reject
-skip string
comma-separated list of kinds or GVKs to ignore
-strict
disallow additional properties not in schema or duplicated keys
-summary
print a summary at the end (ignored for junit output)
Use-cases of Kuberconform
There are different use cases where Kubeconfrom can play a good role. One is regarding Kubernetes upgrades, sometimes you need to ensure that your current manifest is still going to work in the new release that the cluster will be upgraded to, and with this tool, we can ensure that our YAML is still compatible with the latest version directly getting it from the environment and validate it properly.
Another notable aspect of Kubeconform is its seamless integration into existing CI/CD pipelines. You can easily incorporate kubeconform as a step in your pipeline to automatically validate Kubernetes configuration files before deploying them. By doing so, you can catch configuration errors early in the development process, reduce the risk of deployment failures, and maintain high configuration consistency.
In addition to its validation capabilities, kubeconform provides helpful feedback and suggestions for improving your Kubernetes configuration files. It highlights specific issues or deviations from the defined rules and offers guidance on addressing them. This simplifies the troubleshooting process and helps developers and administrators become more familiar with best practices and Kubernetes configuration standards.
Conclusion
Kubeconform is an invaluable utility for Kubernetes users who strive for reliable and consistent deployments. It empowers teams to maintain a high standard of configuration quality, reduces the likelihood of misconfigurations, and improves the overall stability and security of Kubernetes-based applications.
The second -schema-location points at the community CRDs catalog, which covers the popular operators (cert-manager, Prometheus Operator, Istio, Argo). For in-house CRDs, generate schemas once with openapi2jsonschema from your CRD YAML and commit them to the repo — validation then works fully offline. Pin -kubernetes-version to what you actually run: it is the difference between “valid YAML” and “valid on your cluster”.
Frequently Asked Questions
What is the difference between kubeconform and kubeval?
Kubeconform is the maintained successor to kubeval: same idea (validate manifests against the Kubernetes OpenAPI schemas) but with up-to-date schemas, CRD support via -schema-location, parallel validation and active development. Kubeval has been archived; new pipelines should use kubeconform.
Can kubeconform validate CRDs (Custom Resource Definitions)?
Yes, with an extra step: custom resources need their schemas available. Point -schema-location at the CRDs-catalog project or generate JSON schemas from your CRDs with openapi2jsonschema, and kubeconform validates custom resources like any core object. Unknown kinds can be skipped with -ignore-missing-schemas while you build the catalog.
How do I run kubeconform against Helm charts?
Render first, validate after: helm template mychart | kubeconform -strict -summary. In CI this is the standard pattern — template with production-like values, pipe to kubeconform, and fail the pipeline on error before anything reaches a cluster.
Does kubeconform check that my manifests match my cluster version?
Yes — -kubernetes-version 1.33.0 validates against that version’s schemas, which catches fields added in newer APIs or removed in the version you actually run. Run it once per supported cluster version if you maintain a range.
Istio secures traffic with three resources that answer three different questions.PeerAuthentication decides whether workloads must present mTLS identity to each other (“which workload is calling?”). RequestAuthentication validates end-user JWTs on incoming requests (“which user is behind the call?”). AuthorizationPolicy decides who is allowed to do what once identity is established. Most production meshes use all three together: mesh-wide STRICT mTLS, JWT validation at the gateway, and allow-list authorization per namespace.
The rest of this article goes through each resource in detail — modes, selectors, rule semantics and the operational gotchas.
Istio Security Policies are crucial in securing microservices within a service mesh environment. We have discussed Istio and the capabilities that it can introduce to your Kubernetes workloads. Still, today we’re going to be more detailed regarding the different objects and resources that would help us make our workloads much more secure and enforce the communication between them. These objects include PeerAuthentication, RequestAuthentication, and AuthorizationPolicy objects.
PeerAuthentication: Enforcing security on pod-to-pod communication
PeerAuthentication focuses on securing communication between services by enforcing mutual TLS (Transport Layer Security) authentication and authorization. It enables administrators to define authentication policies for workloads based on the source of the requests, such as specific namespaces or service accounts. Configuring PeerAuthentication ensures that only authenticated and authorized services can communicate, preventing unauthorized access and man-in-the-middle attacks. This can be achieved depending on the value of the mode where defining this object being STRICT for only allowed mTLS communication, PERMISSIVE to allow both kinds of communication, DISABLE to forbid the mTLS connection and keep the traffic insecure, and UNSET to use the inherit option. This is a sample of the definition of the object:
RequestAuthentication: Defining authentication methods for Istio Workloads
RequestAuthentication, on the other hand, provides fine-grained control over the authentication of inbound requests. It allows administrators to specify rules and requirements for validating and authenticating incoming requests based on factors like JWT (JSON Web Tokens) validation, API keys, or custom authentication methods. With RequestAuthentication, service owners can enforce specific authentication mechanisms for different endpoints or routes, ensuring that only authenticated clients can access protected resources. Here you can see a sample of a RequestAuthentication object:
As commented, JWT validation is the most used approach as JWT tokens are becoming the de-facto industry standard for incoming validations and the OAuth V2 authorization protocol. Here you can define the rules the JWT needs to meet to be considered a valid request. But the RequestAuthentication only describes the “authentication methods” supported by the workloads but doesn’t enforce it or provide any details regarding the Authorization.
That means that if you define a workload to need to use JWT authentication, sending the request with the token will validate that token and ensure it is not expired. It meets all the rules you have specified in the object definition, but it will also allow bypassing requests with no token at all, as you’re just defining what the workloads support but not enforcing it. To do that, we need to introduce the last object of this set, the AuthorizationPolicy object.
AuthorizationPolicy: Fine-grained Authorization Policy Definition for Istio Policies
AuthorizationPolicy offers powerful access control capabilities to regulate traffic flow within the service mesh. It allows administrators to define rules and conditions based on attributes like source, destination, headers, and even request payload to determine whether a request should be allowed or denied. AuthorizationPolicy helps enforce fine-grained authorization rules, granting or denying access to specific resources or actions based on the defined policies. Only authorized clients with appropriate permissions can access specific endpoints or perform particular operations within the service mesh. Here you can see a sample of an Authorization Policy object:
Here you can go as detailed as you need; you can apply rules on the source of the request to ensure that only some recommendations can go through (for example, requests that are from a JWT token to use in combination with the RequestAuthenitcation object), but also rules on the target, if this is going to a specific host or path or method or a combination of both. Also, you can apply to ALLOW rules or DENY rules (or even CUSTOM) and define a set of them, and all of them will be enforced as a whole. The evaluation is determined by the following rules as stated in the Istio Official Documentation:
If there are any CUSTOM policies that match the request, evaluate and deny the request if the evaluation result is denied.
If there are any DENY policies that match the request, deny the request.
If there are no ALLOW policies for the workload, allow the request.
If any of the ALLOW policies match the request, allow the request. Deny the request.
This will provide all the requirements you could need to be able to do a full definition of all the security policies needed.
Conclusion
In conclusion, Istio’s Security Policies provide robust mechanisms for enhancing the security of microservices within a service mesh environment. The PeerAuthentication, RequestAuthentication, and AuthorizationPolicy objects offer a comprehensive toolkit to enforce authentication and authorization controls, ensuring secure communication and access control within the service mesh. By leveraging these Istio Security Policies, organizations can strengthen the security posture of their microservices, safeguarding sensitive data and preventing unauthorized access or malicious activities within their service mesh environment.
Frequently Asked Questions
What is the difference between PeerAuthentication and RequestAuthentication in Istio?
PeerAuthentication controls service-to-service identity: whether workloads require mTLS from their peers. RequestAuthentication validates end-user credentials — JWTs on incoming requests. They answer different questions (“which workload is calling?” vs “which user is behind the call?”) and are usually combined.
Does RequestAuthentication reject requests without a token?
No — by itself it only validates tokens that are present and strips invalid ones. To actually require a token you pair it with an AuthorizationPolicy that denies requests without a requestPrincipal. Forgetting this second half is the most common Istio auth misconfiguration.
How does AuthorizationPolicy decide allow or deny?
Evaluation order: CUSTOM policies first, then DENY, then ALLOW. If any ALLOW policy exists for a workload, everything not explicitly allowed is denied. With no policies at all, everything is allowed — so the first ALLOW policy you add flips the default for that workload.
Can I enforce mTLS for the whole mesh at once?
Yes — a mesh-wide PeerAuthentication in the root namespace with mtls.mode: STRICT. Roll it out with PERMISSIVE first and watch telemetry for plaintext connections, then switch to STRICT once nothing legitimate is left.