Introduction
Almost every team that exposes a service from a Kubernetes cluster runs into the same first question: which ingress controller should we use. And in the search for an answer, two candidates come up for comparison more than any others — Traefik and ingress-nginx.
On the surface they do the same thing. They receive inbound HTTP/HTTPS traffic from the outside world and route it to the appropriate service inside the cluster. But dig a little deeper and the two projects differ substantially — in their starting points, their design philosophy, the way they express configuration, the mechanism they use to handle dynamic changes, and the position they occupy in the ecosystem.
I have run both controllers in production, across on-premises clusters for financial-sector customers and a cloud-based AI serving platform. In that process I learned that "which one is better" is actually the wrong question. The more accurate question is "which one fits our team's operating model and requirements better."
In 2026 there is one more important piece of context to add. The Kubernetes Ingress API is functionally frozen — no new features are being added. Its successor standard, Gateway API, has reached GA and has effectively become the next-generation standard. On top of that, the ingress-nginx project has moved into maintenance mode, and it carries a history of several significant security issues. These changes are shifting the center of gravity in controller selection.
In this article I compare the two controllers in depth, from design philosophy to practical operational detail, show side-by-side YAML for how the same requirement is implemented on each side, and finish with scenario-based recommendations and a decision table.
The Identity of Each Controller
ingress-nginx — a Thin Control Layer on Top of Proven NGINX
First, some terminology, because "nginx ingress" can refer to two different projects and causes a lot of confusion.
- **ingress-nginx**: the controller maintained by the Kubernetes community project. It is the subject of this article and the most widely used option.
- **nginx-ingress**: a separate commercial/open-source controller maintained by NGINX (F5). It has a different annotation scheme and CRDs.
In this article, "ingress-nginx" means the former — the community project maintained at kubernetes.github.io/ingress-nginx.
The core idea of ingress-nginx is: "use NGINX, which has been proven for decades, as the data plane, and put a thin control layer on top that watches Kubernetes resources and generates nginx.conf." When the controller detects a change in an Ingress resource, it produces a new nginx.conf through templating and tells NGINX to reload.
The advantages of this design are clear. NGINX is an industry standard for performance and stability, and operators are already familiar with it. The drawbacks are equally clear. Fine-grained behavior control comes to depend on annotations on the Ingress resource, and complex requirements tend to drift toward injecting raw nginx.conf snippets.
Traefik — a Proxy Designed for Cloud Native from the Start
Traefik is a reverse proxy written in Go, designed from the ground up for dynamic, container-centric environments. Since the Docker days, its core value proposition has been: "in an environment where services come and go, routing updates itself automatically without a human rewriting a config file."
Traefik configuration splits into static configuration and dynamic configuration. Static configuration covers things decided at process startup — EntryPoints (listening ports), providers, logging — while dynamic configuration covers routing rules that keep changing at runtime — Routers, Services, Middlewares. In Kubernetes you can express this dynamic configuration either through Ingress resources or through Traefik's own CRDs (IngressRoute, Middleware, and so on).
The key differentiator is that Traefik applies dynamic configuration **without disruption**. Even when routing rules change, no process reload or restart is required. This is the most fundamental design difference separating the two controllers.
Comparing Design Philosophy
If we compress the difference into a single table, it looks like this.
| Aspect | ingress-nginx | Traefik |
| --- | --- | --- |
| Data plane | NGINX (C-based) | Native Go proxy |
| Starting point | K8s adapter over existing NGINX | Cloud-native by design |
| Config expression | Ingress + annotations + snippets | Ingress or CRD (IngressRoute/Middleware) |
| Config application | Generate nginx.conf, then reload | Disruption-free dynamic apply |
| Middleware model | Annotation/snippet based | Declarative Middleware chain |
| Automatic TLS | Separate cert-manager setup | Built-in ACME (Let's Encrypt) |
| Observability | Metrics/logs (much extra setup) | Built-in metrics/tracing/dashboard |
| Config philosophy | Closer to imperative | Declaration-oriented |
This table alone reveals that the two projects approach the same problem with very different mindsets. ingress-nginx focuses on "connecting a powerful, familiar engine to Kubernetes," while Traefik focuses on "assimilating the proxy itself into Kubernetes's declarative model."
Configuration Style — Annotations vs CRD/Middleware
This is the area where the felt difference is largest in day-to-day operations.
The ingress-nginx Annotation Model
In ingress-nginx, the body of an Ingress resource expresses only simple host/path routing, while nearly every additional behavior (redirects, rewrites, auth, rate limiting, timeouts, and so on) is specified via annotations.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web-app
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
nginx.ingress.kubernetes.io/ssl-redirect: 'true'
nginx.ingress.kubernetes.io/proxy-body-size: 50m
nginx.ingress.kubernetes.io/rate-limit-rps: '20'
nginx.ingress.kubernetes.io/configuration-snippet: |
more_set_headers "X-Custom: hello";
spec:
ingressClassName: nginx
rules:
- host: app.example.com
http:
paths:
- path: /web(/|$)(.*)
pathType: ImplementationSpecific
backend:
service:
name: web-svc
port:
number: 80
This approach is very fast and intuitive in simple cases. But as requirements grow more complex, annotation keys pile up in long lists, and eventually you end up injecting raw nginx.conf fragments via configuration-snippet or server-snippet. Snippet injection is powerful but also the most dangerous part. A single malformed snippet can fail an entire reload, and in the past it has even become a path for security bypasses. In fact, the serious ingress-nginx vulnerabilities reported in 2025 were closely related to this snippet/config-injection path.
The Traefik CRD and Middleware Model
Traefik expresses additional behavior not through annotations but through Middleware — an independent, declarative resource. And routing itself can be expressed more richly through the IngressRoute CRD.
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: rate-limit
spec:
rateLimit:
average: 20
burst: 40
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: custom-header
spec:
headers:
customRequestHeaders:
X-Custom: hello
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: web-app
spec:
entryPoints:
- websecure
routes:
- match: Host(`app.example.com`) && PathPrefix(`/web`)
kind: Rule
middlewares:
- name: rate-limit
- name: custom-header
services:
- name: web-svc
port: 80
Do you see the difference. In Traefik, a policy such as rate-limit is a reusable object that multiple routes can reference, and you can even separate permissions with RBAC. Routing match rules are also written as expressions that combine Host and PathPrefix with logical operators. This is easier to validate and test than annotation strings, and it is friendlier for tracking change history in a GitOps environment.
There is a cost, of course. You must install CRDs in the cluster, and your team must learn Traefik-specific resources rather than the Ingress standard. You can use Traefik with standard Ingress resources too, but doing so forfeits a good portion of Traefik's strengths.
Automatic TLS
Issuing and renewing HTTPS certificates is an unavoidable operational topic. The two controllers approach it very differently.
Traefik's Built-in ACME
Traefik builds integration with ACME providers like Let's Encrypt directly into the controller. Define a certificate resolver in the static configuration, and it automatically issues and renews certificates for domains arriving at an EntryPoint.
Traefik static configuration (part of values.yaml)
certificatesResolvers:
letsencrypt:
acme:
email: ops@example.com
storage: /data/acme.json
httpChallenge:
entryPoint: web
Specify this resolver in your IngressRoute and you are done. No separate controller installation is required.
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: secure-app
spec:
entryPoints:
- websecure
routes:
- match: Host(`secure.example.com`)
kind: Rule
services:
- name: secure-svc
port: 80
tls:
certResolver: letsencrypt
That said, this built-in ACME often assumes single-instance storage (acme.json), so scaling out to multiple replicas requires distributed storage or external certificate management. In a high-availability setup this can become a trap.
ingress-nginx + cert-manager
ingress-nginx does not build certificate issuance into itself. Instead it pairs with cert-manager, the de facto standard. cert-manager declares certificates with the Certificate/Issuer CRDs, stores issued certificates as Secrets, and ingress-nginx references those Secrets.
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: ops@example.com
privateKeySecretRef:
name: letsencrypt-prod-key
solvers:
- http01:
ingress:
class: nginx
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: secure-app
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
ingressClassName: nginx
tls:
- hosts:
- secure.example.com
secretName: secure-app-tls
rules:
- host: secure.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: secure-svc
port:
number: 80
At first glance it looks more complex because there is one more component, but in practice this separation tends to be an advantage. cert-manager broadly supports HA environments, wildcard certificates, the DNS-01 challenge, and integration with internal PKI (Vault, private ACME CA). Because certificate management responsibility is separated from the controller, you can keep reusing the certificate infrastructure even if you swap controllers.
Performance and Resources
Performance is the most frequently debated yet the hardest area to generalize. Exact numbers vary enormously with workload, connection patterns, and configuration, so it is more important to understand tendencies and characteristics than absolute values.
| Characteristic | ingress-nginx | Traefik |
| --- | --- | --- |
| Data plane language | C (NGINX) | Go |
| Static high-load handling | Very strong (proven NGINX) | Strong |
| Memory per connection | Generally low | May vary due to GC |
| Cost of config change | Triggers reload | Disruption-free (no reload) |
| Bulk route changes | Heavy if reloads are frequent | Almost free |
NGINX, the data plane of ingress-nginx, shows performance proven over years in static, stable high-load environments. Traefik, due to Go's garbage collection, may exhibit slight latency variation under extreme load, but for most real-world workloads the difference is hard to feel.
The more important performance difference in practice is "the cost of a configuration change." In environments where the Ingress changes often, ingress-nginx triggers reloads frequently. In environments with hundreds of microservices and frequent deployments, or multi-tenant platforms where routes are created and deleted dynamically, the reload itself can become a burden. The next section examines this difference in detail.
Dynamic Configuration and Reload Differences
This difference is the most essential distinction between the two controllers, so let us look at it separately and in depth.
The ingress-nginx Reload Model
When ingress-nginx detects a change in Ingress/Service/Endpoint, it operates in the following flow.
[K8s API] --watch--> [ingress-nginx controller]
|
v
generate new nginx.conf (template)
|
v
NGINX reload (nginx -s reload)
|
v
new worker processes apply new config
NGINX's reload is graceful. Existing workers finish in-flight connections and exit, and new workers receive traffic with the new config. But if reloads are very frequent, worker processes are continually created and destroyed, memory usage fluctuates, and long-lived connections (WebSocket, gRPC streams) can be affected at reload time. To mitigate this, ingress-nginx introduced an optimization that applies endpoint-only changes via Lua without a reload, but when the Ingress structure itself changes a reload is still required.
Traefik's Disruption-free Application
Traefik manages dynamic configuration as an in-memory routing table. When a CRD or Ingress changes, it atomically swaps in the new routing rules — there is no process reload and no worker recreation.
[K8s API] --watch--> [Traefik provider]
|
v
update dynamic config (in memory)
|
v
atomic swap of routing table
|
(no connection impact, no reload)
In environments where routes change dozens of times per second — for instance a serverless platform where routes are created and deleted per function, or a large multi-tenant SaaS — this disruption-free characteristic becomes a decisive advantage.
Observability
Half of operations is "seeing what is happening right now."
Traefik's Built-in Observability
Traefik builds Prometheus metrics, distributed tracing (OpenTelemetry), access logs, and a web dashboard into the controller itself. With one or two lines of configuration you can turn on a metrics endpoint and the dashboard.
Traefik static configuration (part)
metrics:
prometheus:
addEntryPointsLabels: true
addServicesLabels: true
tracing:
otlp:
grpc:
endpoint: otel-collector:4317
api:
dashboard: true
In the dashboard you can see currently registered routers, services, middlewares, and health status at a glance, so you can quickly confirm "how my routing rules were actually applied." However, the dashboard must always be protected with authentication in production.
ingress-nginx Observability
ingress-nginx also provides Prometheus metrics and can leverage NGINX's rich log variables. Tracing and a dashboard, however, are not built in by default and largely require separate setup (for example, enabling a tracing module or importing a Grafana dashboard). On the other hand, being able to reuse the mature logging/monitoring assets of the NGINX ecosystem is a strength. Per-VirtualServer/Upstream metrics, response-time histograms, and the like are things SREs can handle comfortably.
Ecosystem and Governance
Relationship to Gateway API
This is the most important context in 2026. The Kubernetes Ingress API is frozen — no new features are being added — and Gateway API has established itself as the next-generation standard. Gateway API offers role-based separation (Infrastructure Provider / Cluster Operator / Application Developer), expressive routing, and a unified L4/L7 model.
- **Traefik**: supports Gateway API as a provider, and you can use it alongside its own CRD (IngressRoute). It is actively embracing the cloud-native standard.
- **ingress-nginx**: as the project moved into maintenance mode, a migration trend is forming toward Gateway-API-based successor projects in the Kubernetes ecosystem (for example, Gateway API implementations such as NGINX Gateway Fabric). For a new adoption, this is the time to seriously consider a Gateway-API-based implementation.
In short, over the long term both camps are converging on Gateway API, and for a new project you should consider "Ingress or Gateway API" together with the controller choice.
Maintenance State and Security
ingress-nginx has been extremely widely used, but it has a history of insufficient project maintenance resources, a move into maintenance mode, and serious security vulnerabilities related to the config-injection path (for example, a series of CVEs disclosed in 2025). This does not mean ingress-nginx is bad — it means you must evaluate the security operations burden and long-term roadmap before adopting it anew. Traefik is a project with active commercial backing (Traefik Labs), with steady feature additions and documentation.
Learning Curve
| Perspective | ingress-nginx | Traefik |
| --- | --- | --- |
| Initial entry | Easy (just know the Ingress standard) | Moderate (must learn CRD concepts) |
| Simple routing | Very fast | Fast |
| Complex policies | Steepens as annotations/snippets pile up | Expands gently with Middleware |
| Troubleshooting | Needs nginx.conf debugging knowledge | Intuitive via the dashboard |
| Standard affinity | The Ingress standard as-is | Choice of Ingress or CRD |
ingress-nginx has a low barrier to entry because you can start immediately if you know standard Ingress. But as requirements grow complex, annotations and snippets accumulate and difficulty rises steeply. Traefik has some learning cost upfront because you need to learn the CRD and static/dynamic configuration concepts, but once you do, even complex policies expand gently.
Implementing the Same Requirement on Both Sides
Enough theory. Let us look side by side at how the same requirement is implemented on each side. The requirements are as follows.
1. Route the /api path of app.example.com to api-svc, and everything else to web-svc.
2. Force a redirect from HTTP to HTTPS.
3. Apply Basic Auth to the /api path.
4. Limit requests per second.
ingress-nginx Implementation
apiVersion: v1
kind: Secret
metadata:
name: api-basic-auth
type: Opaque
data:
user/password generated with htpasswd (base64)
auth: dXNlcjokYXByMSRleGFtcGxlaGFzaA==
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-ingress
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: 'true'
nginx.ingress.kubernetes.io/limit-rps: '20'
spec:
ingressClassName: nginx
tls:
- hosts:
- app.example.com
secretName: app-tls
rules:
- host: app.example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api-svc
port:
number: 80
- path: /
pathType: Prefix
backend:
service:
name: web-svc
port:
number: 80
Since Basic Auth must apply only to the /api path, the common pattern in ingress-nginx is to split the path requiring auth into a separate Ingress object and attach the auth annotations only to that object.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-api-auth
annotations:
nginx.ingress.kubernetes.io/auth-type: basic
nginx.ingress.kubernetes.io/auth-secret: api-basic-auth
nginx.ingress.kubernetes.io/auth-realm: 'Authentication Required'
nginx.ingress.kubernetes.io/ssl-redirect: 'true'
spec:
ingressClassName: nginx
rules:
- host: app.example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api-svc
port:
number: 80
You can see that when policies differ per path, you must split the Ingress object or the annotations grow complex.
Traefik Implementation
apiVersion: v1
kind: Secret
metadata:
name: api-basic-auth
type: Opaque
data:
users: dXNlcjokYXByMSRleGFtcGxlaGFzaA==
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: api-auth
spec:
basicAuth:
secret: api-basic-auth
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: ratelimit
spec:
rateLimit:
average: 20
burst: 40
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: https-redirect
spec:
redirectScheme:
scheme: https
permanent: true
Now we compose middlewares per path in the routing. The HTTPS redirect applies at the HTTP EntryPoint (web), while auth and rate limiting apply per path.
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: example-route
spec:
entryPoints:
- websecure
routes:
- match: Host(`app.example.com`) && PathPrefix(`/api`)
kind: Rule
middlewares:
- name: api-auth
- name: ratelimit
services:
- name: api-svc
port: 80
- match: Host(`app.example.com`)
kind: Rule
middlewares:
- name: ratelimit
services:
- name: web-svc
port: 80
tls:
certResolver: letsencrypt
The difference is clear. In Traefik, policies (auth, rate limiting, redirect) are separated into reusable objects, and a route composes the policies it needs. Even when applying different policies per path, there is no need to split the Ingress object. ingress-nginx, by contrast, is more concise in simple cases, but as per-path policy branching grows, splitting objects and accumulating annotations becomes unavoidable.
Operations and Tuning
ingress-nginx Operational Points
- **worker-processes / worker-connections**: tune NGINX worker settings via ConfigMap. Tune the worker count to fit node resources.
- **proxy buffers/timeouts**: adjust proxy-body-size, proxy-read-timeout, and so on to handle large uploads or slow backends.
- **reload frequency management**: if deployments are frequent, verify that endpoint changes are handled via the Lua path, and reduce unnecessary Ingress changes.
- **keepalive**: tune upstream keepalive to increase connection reuse.
apiVersion: v1
kind: ConfigMap
metadata:
name: ingress-nginx-controller
data:
worker-processes: 'auto'
max-worker-connections: '16384'
proxy-body-size: '100m'
upstream-keepalive-connections: '320'
use-gzip: 'true'
Traefik Operational Points
- **EntryPoint tuning**: set timeouts (readTimeout, writeTimeout, idleTimeout) at the EntryPoint level.
- **Replicas and ACME**: scaling out while using built-in ACME requires a strategy for sharing certificate storage. For HA, consider cert-manager or external issuance.
- **Provider throttle**: when configuration changes flood in, batch updates with providersThrottleDuration.
- **Dashboard security**: in production, always protect the dashboard with authentication/network policy.
Traefik static configuration (part)
entryPoints:
websecure:
address: ':443'
transport:
respondingTimeouts:
readTimeout: 60s
writeTimeout: 60s
idleTimeout: 180s
providers:
kubernetesCRD:
allowCrossNamespace: false
providersThrottleDuration: 2s
Pitfalls and Troubleshooting
Here are pitfalls I have actually run into in operations.
Common Pitfalls in ingress-nginx
- **Snippet injection disabled**: for security hardening, allow-snippet-annotations is trending toward being disabled by default. Configurations that depended on snippets can suddenly be ignored, leading to incidents. Always check on upgrade.
- **rewrite-target and regex**: the combination of rewrite-target and path capture groups frequently causes mistakes. You must understand pathType and regex behavior precisely.
- **reload storms**: in large environments, frequent deployments trigger reloads, memory fluctuates, and long-lived connections can drop.
- **CVE response**: you must follow security patch releases quickly. Under maintenance mode, reflect the patch cadence in your operations policy.
Common Pitfalls in Traefik
- **CRD version mismatch**: if the traefik.io API version and the chart version diverge, IngressRoute is ignored. Always match the installed CRD version to the chart.
- **ACME storage contention**: sharing acme.json across multiple replicas causes issuance contention. For HA, a separate certificate strategy is safer.
- **Cross-namespace references**: referencing a middleware from another namespace requires explicit allow settings. The default is to block.
- **Dashboard exposure**: exposing the dashboard externally without authentication is a common incident. Always protect it.
Common Debugging Flow
Check controller logs
kubectl logs -n ingress-nginx deploy/ingress-nginx-controller
kubectl logs -n traefik deploy/traefik
Check actually applied config (ingress-nginx)
kubectl exec -n ingress-nginx deploy/ingress-nginx-controller -- cat /etc/nginx/nginx.conf
Check route status (Traefik dashboard or API)
kubectl port-forward -n traefik deploy/traefik 8080:8080
Check ingress resources and events
kubectl describe ingress example-ingress
kubectl get events --sort-by=.lastTimestamp
Migration Considerations
Things to consider when moving from one to the other.
- **Annotation vs CRD mapping**: there are cases where ingress-nginx annotations are hard to map one-to-one onto Traefik Middleware. You must redesign policies such as rewrite, auth, and rate-limit as middlewares.
- **Running with separate IngressClass**: you can run both controllers at once and gradually shift traffic via IngressClass for a disruption-free migration.
- **Reusing TLS infrastructure**: if you were using cert-manager, configure Traefik to reference the Secrets that cert-manager created, and you can move without reissuing certificates.
- **Considering Gateway API at the same time**: if you are migrating, it is advantageous in the long run to also evaluate going straight to Gateway API instead of Ingress.
Decision Table
| Situation/Requirement | Recommendation |
| --- | --- |
| Simple routing with standard Ingress only | ingress-nginx or a Gateway API implementation |
| Many complex, reusable per-path policies | Traefik (Middleware) |
| Frequent disruption-free dynamic routing (serverless/multi-tenant) | Traefik |
| Want built-in automatic TLS and a dashboard | Traefik |
| HA wildcard/DNS-01/internal-PKI certificates | ingress-nginx + cert-manager |
| Team with deep NGINX operations experience | ingress-nginx |
| New greenfield project (long-term standard focus) | Consider Gateway API first |
| Minimizing security operations burden is top priority | An actively maintained option (Traefik/Gateway implementation) |
Closing
Let us return to the beginning. The question "which is better, Traefik or ingress-nginx" has no single answer. The two controllers solve the same problem with different philosophies.
Summarized by scenario:
- **A team familiar with NGINX whose primary need is simple routing** still finds ingress-nginx a fast, familiar choice. But the maintenance-mode status and the security-patch burden must be reflected in your operations policy.
- **If you need refined per-path policies, disruption-free dynamic routing, and built-in TLS/observability**, Traefik fits more naturally. Pay the CRD learning cost once, and you can handle complexity gently.
- **If HA certificate management, wildcards, and internal PKI are central**, a configuration with cert-manager as the certificate layer is stable regardless of which controller you use.
- **For a greenfield project starting fresh in 2026**, face the fact that the Ingress API is frozen, and I recommend considering a Gateway-API-based implementation as your first choice. Traefik supports Gateway API, and the ingress-nginx camp is also moving toward Gateway API implementations.
In the end, the key is to choose at the intersection of "your team's operating model, the complexity of your requirements, and your long-term standards strategy." A controller is only a tool, and a good choice comes not from the superiority of the tool but from its fit with the context.
References
- [Kubernetes Ingress official documentation](https://kubernetes.io/docs/concepts/services-networking/ingress/)
- [Kubernetes Ingress Controllers overview](https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/)
- [ingress-nginx official documentation](https://kubernetes.github.io/ingress-nginx/)
- [ingress-nginx annotations reference](https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/)
- [Traefik Proxy official documentation](https://doc.traefik.io/traefik/)
- [Traefik Kubernetes IngressRoute provider](https://doc.traefik.io/traefik/providers/kubernetes-crd/)
- [Gateway API official documentation](https://gateway-api.sigs.k8s.io/)
- [cert-manager official documentation](https://cert-manager.io/docs/)
- [Helm official documentation](https://helm.sh/docs/)
- [Let's Encrypt official documentation](https://letsencrypt.org/docs/)
현재 단락 (1/415)
Almost every team that exposes a service from a Kubernetes cluster runs into the same first question...