← Back to Blog

Proxies for Kubernetes Ingress Controller: Complete Setup in K8s Cluster with Configuration Examples

We discuss how to properly configure a proxy server in Kubernetes Ingress Controller β€” from basic NGINX configuration to rotating resident IPs to bypass restrictions.

πŸ“…August 10, 2026

If your K8s cluster makes external requests β€” to partner APIs, marketplaces, advertising platforms β€” sooner or later you will encounter IP blocks. The Ingress Controller itself manages incoming traffic, but a separate scheme with a proxy is needed for outgoing requests through pods. In this article, we discuss how to set this up correctly: from choosing the type of proxy to specific YAML configs for NGINX Ingress and Traefik.

Why use a proxy in Kubernetes: real scenarios

Kubernetes is an orchestrator, and most articles about it discuss incoming traffic: how to set up Ingress, how to issue a TLS certificate, how to route requests to services. But there is another side: outgoing traffic from pods. This is where problems with blocks, geo-restrictions, and IP limits arise.

Let’s consider specific tasks where a proxy in a K8s cluster becomes a necessity:

  • Price parsing and monitoring. Your cluster runs a CronJob every 15 minutes and collects data from Wildberries, Ozon, Amazon. Without IP rotation, after 2–3 hours you will receive a ban across the entire IP range of your cloud provider.
  • Working with advertising APIs. Facebook Marketing API, TikTok Ads API, Google Ads API β€” all of them track the source of requests. If dozens of pods send requests from one data center IP, this is a trigger for automatic blocking.
  • Geolocation testing. Your service must show different content for users from different countries. Through a proxy, pods can simulate requests from the required regions for automated tests.
  • Bypassing corporate restrictions. In some infrastructures, all outgoing traffic must go through a corporate proxy β€” this is a compliance requirement.
  • Working with partner APIs with whitelisted IPs. When a partner allows requests only from specific IPs, and your cluster scales and changes addresses β€” a proxy with static IPs solves the problem.

It is important to understand the architectural separation: The Ingress Controller manages incoming traffic (from users to your services), while the proxy for outgoing requests is a separate task. Nevertheless, they are related: the proxy configuration is often set at the level of the same Ingress Controller or through annotations that it reads.

πŸ’‘ Key distinction

In this article, we discuss two scenarios: (1) configuring the Ingress Controller as a reverse proxy to external upstream servers, and (2) routing outgoing traffic from pods through an external proxy server. Both scenarios are encountered in production.

Which type of proxy to choose for a K8s cluster

The choice of proxy type directly affects how long your cluster can operate without blocks. Data center IPs are the cheapest, but they are easily identifiable and blockable. Residential and mobile IPs are more expensive but significantly more reliable for tasks where simulating a real user is important.

Proxy Type Speed Risk of Blocking Best Scenario in K8s
Data Center High High Internal APIs, corporate compliance, partner integrations with whitelisted IPs
Residential Medium Low Parsing marketplaces, working with advertising APIs, geo-testing
Mobile Medium Minimal Facebook Ads API, TikTok API, high-load tasks with ban risk

For most tasks in a K8s cluster related to parsing or working with advertising platforms, the optimal choice is residential proxies with rotation. They provide real IPs of home users, and website protection algorithms perceive such requests as regular browser traffic.

From a protocol perspective, the most universal for K8s is HTTP/HTTPS proxy β€” it is supported by all popular HTTP clients in any programming language through standard environment variables HTTP_PROXY and HTTPS_PROXY. SOCKS5 is only needed for non-standard protocols or when proxying is required for non-HTTP traffic.

Setting up a proxy through NGINX Ingress Controller

NGINX Ingress Controller is the most common option in K8s. Let’s consider two scenarios: configuring NGINX as a reverse proxy to an external upstream through a proxy server, and global configuration of outgoing proxy for the controller itself.

Annotations for proxying requests to upstream

NGINX Ingress supports annotations to manage proxy behavior. If you need Ingress to forward requests to an external service through an intermediate proxy, use a ConfigMap with a custom config:

apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-ingress-controller
  namespace: ingress-nginx
data:
  # Global HTTP proxy for outgoing requests from NGINX
  http-snippet: |
    proxy_connect_timeout 10s;
    proxy_read_timeout 30s;
    proxy_send_timeout 30s;
  # Upstream configuration through proxy
  use-proxy-protocol: "false"
  proxy-real-ip-cidr: "0.0.0.0/0"

Ingress resource with annotations for proxy

For a separate Ingress resource, you can set proxy parameters through annotations. This is useful when different services require different timeouts and settings:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-service-ingress
  namespace: production
  annotations:
    kubernetes.io/ingress.class: "nginx"
    # Proxy timeouts
    nginx.ingress.kubernetes.io/proxy-connect-timeout: "10"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "60"
    # Buffer size
    nginx.ingress.kubernetes.io/proxy-buffer-size: "16k"
    nginx.ingress.kubernetes.io/proxy-buffers-number: "8"
    # Forwarding the real client IP
    nginx.ingress.kubernetes.io/use-forwarded-headers: "true"
    nginx.ingress.kubernetes.io/forwarded-for-header: "X-Forwarded-For"
spec:
  rules:
  - host: myapp.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: my-service
            port:
              number: 80

Configuring an external proxy for outgoing requests from the NGINX pod

If the NGINX Ingress Controller itself needs to make outgoing requests through an external proxy (for example, for health-checking external endpoints), you need to set environment variables in the controller's Deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: ingress-nginx-controller
  namespace: ingress-nginx
spec:
  template:
    spec:
      containers:
      - name: controller
        image: registry.k8s.io/ingress-nginx/controller:v1.9.4
        env:
        - name: HTTP_PROXY
          valueFrom:
            secretKeyRef:
              name: proxy-credentials
              key: http-proxy-url
        - name: HTTPS_PROXY
          valueFrom:
            secretKeyRef:
              name: proxy-credentials
              key: https-proxy-url
        - name: NO_PROXY
          value: "localhost,127.0.0.1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,.cluster.local"

Note the NO_PROXY variable β€” it is critically important. Without it, all intra-cluster traffic will also go through the external proxy, which will break the interaction between services. Always include RFC-1918 ranges and the suffix .cluster.local in NO_PROXY.

Setting up a proxy in Traefik Ingress Controller

Traefik is a popular alternative to NGINX Ingress, especially in conjunction with Helm and in managed clusters (k3s, Docker Swarm, Nomad). Traefik has its own configuration system through CRD (Custom Resource Definitions) and static/dynamic configs.

Static configuration of Traefik with proxy

apiVersion: v1
kind: ConfigMap
metadata:
  name: traefik-config
  namespace: traefik
data:
  traefik.yaml: |
    # Static configuration of Traefik
    entryPoints:
      web:
        address: ":80"
      websecure:
        address: ":443"
    
    # Settings for working behind a proxy
    serversTransport:
      insecureSkipVerify: false
      maxIdleConnsPerHost: 200
      dialTimeout: 10s
      responseHeaderTimeout: 30s
    
    # Logging for proxy diagnostics
    log:
      level: INFO
    
    accessLog:
      fields:
        headers:
          defaultMode: keep
          names:
            X-Forwarded-For: keep
            X-Real-IP: keep

Middleware for adding proxy headers

In Traefik, you can create Middleware that will add or modify headers when passing requests. This is useful for correctly passing the real client IP through the proxy chain:

apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: proxy-headers
  namespace: production
spec:
  headers:
    customRequestHeaders:
      X-Forwarded-Proto: "https"
    # Remove headers that may reveal infrastructure
    customResponseHeaders:
      X-Powered-By: ""
      Server: ""
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: my-app-route
  namespace: production
spec:
  entryPoints:
    - websecure
  routes:
  - match: Host(`myapp.example.com`)
    kind: Rule
    middlewares:
    - name: proxy-headers
    services:
    - name: my-service
      port: 80

Environment variables for Traefik Deployment

Similarly to NGINX, for Traefik we set environment variables through Helm values or directly in the Deployment. When using the Helm chart, this is done through values.yaml:

# values.yaml for Traefik Helm chart
deployment:
  enabled: true
  kind: Deployment
  
env:
  - name: HTTP_PROXY
    valueFrom:
      secretKeyRef:
        name: proxy-credentials
        key: http-proxy-url
  - name: HTTPS_PROXY
    valueFrom:
      secretKeyRef:
        name: proxy-credentials
        key: https-proxy-url
  - name: NO_PROXY
    value: "localhost,127.0.0.1,10.96.0.0/12,10.244.0.0/16,.cluster.local,.svc"

Proxy through environment variables in pods

The most universal way to set up an outgoing proxy for any pod is through standard environment variables. This method works regardless of the programming language and framework: Python requests, Go net/http, Node.js axios, Java HttpClient β€” all of them automatically read HTTP_PROXY and HTTPS_PROXY.

Option 1: Directly in Pod Spec

apiVersion: v1
kind: Pod
metadata:
  name: scraper-pod
  namespace: production
spec:
  containers:
  - name: scraper
    image: mycompany/scraper:latest
    env:
    - name: HTTP_PROXY
      value: "http://username:[email protected]:8080"
    - name: HTTPS_PROXY
      value: "http://username:[email protected]:8080"
    - name: NO_PROXY
      value: "localhost,127.0.0.1,10.0.0.0/8,.cluster.local"
    resources:
      requests:
        memory: "128Mi"
        cpu: "100m"
      limits:
        memory: "256Mi"
        cpu: "500m"

⚠️ Do not store credentials in plain text!

The example above is shown for clarity. In production, proxy credentials must always be stored in Kubernetes Secret and passed through secretKeyRef. More details are in the Secrets section below.

Option 2: Through ConfigMap for multiple pods

If the proxy is used by multiple Deployments, it is convenient to move the settings to a ConfigMap and connect it through envFrom:

apiVersion: v1
kind: ConfigMap
metadata:
  name: proxy-config
  namespace: production
data:
  NO_PROXY: "localhost,127.0.0.1,10.0.0.0/8,172.16.0.0/12,.cluster.local,.svc.cluster.local"
  PROXY_TIMEOUT: "30"
  PROXY_MAX_RETRIES: "3"
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: price-monitor
  namespace: production
spec:
  replicas: 5
  selector:
    matchLabels:
      app: price-monitor
  template:
    metadata:
      labels:
        app: price-monitor
    spec:
      containers:
      - name: monitor
        image: mycompany/price-monitor:v2.1
        envFrom:
        - configMapRef:
            name: proxy-config
        - secretRef:
            name: proxy-credentials
        ports:
        - containerPort: 8080

Option 3: Mutating Admission Webhook

For corporate clusters where all pods must use a proxy, you can set up a mutating admission webhook that automatically adds proxy environment variables to all created pods. This solution is used in enterprise infrastructures where compliance requires that all outgoing traffic goes through a corporate proxy. The implementation of such a webhook goes beyond the scope of this article, but it is worth knowing about its existence.

IP rotation and managing the proxy pool in K8s

A static proxy is good for corporate compliance, but bad for tasks where you need to avoid blocks. If 50 of your pods send requests through the same IP, that IP will be blocked very quickly. The solution is proxy rotation.

Proxy Sidecar Pattern

One of the patterns is to run a proxy agent as a sidecar container next to the main application. The sidecar receives requests on localhost:8080 and forwards them through a rotating pool of external proxies:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: scraper-with-proxy-sidecar
  namespace: production
spec:
  replicas: 10
  selector:
    matchLabels:
      app: scraper
  template:
    metadata:
      labels:
        app: scraper
    spec:
      containers:
      # Main application
      - name: scraper
        image: mycompany/scraper:latest
        env:
        - name: HTTP_PROXY
          value: "http://localhost:8080"
        - name: HTTPS_PROXY
          value: "http://localhost:8080"
        - name: NO_PROXY
          value: "localhost,127.0.0.1,.cluster.local"
      
      # Sidecar: local proxy agent with rotation
      - name: proxy-rotator
        image: mycompany/proxy-rotator:latest
        ports:
        - containerPort: 8080
        env:
        - name: PROXY_LIST_URL
          valueFrom:
            secretKeyRef:
              name: proxy-credentials
              key: api-endpoint
        - name: ROTATION_INTERVAL
          value: "60"  # rotation every 60 seconds
        resources:
          requests:
            memory: "64Mi"
            cpu: "50m"
          limits:
            memory: "128Mi"
            cpu: "200m"

Central Proxy Service Pattern

An alternative approach is a single Proxy Service inside the cluster, which all pods refer to. This is easier to manage: just update one Secret with the proxy credentials, and all pods will automatically start using the new data.

apiVersion: v1
kind: Service
metadata:
  name: proxy-gateway
  namespace: proxy-system
spec:
  selector:
    app: proxy-gateway
  ports:
  - name: http
    port: 8080
    targetPort: 8080
  - name: socks5
    port: 1080
    targetPort: 1080
  type: ClusterIP
---
# Pods use the proxy through internal DNS
# HTTP_PROXY=http://proxy-gateway.proxy-system.svc.cluster.local:8080

For tasks where anonymity is critical and the risk of blocks is minimal (for example, parsing marketplaces or working with advertising APIs), we recommend using residential proxies with rotation β€” they provide real IPs of home users, making traffic from your cluster indistinguishable from regular browser traffic.

Storing proxy credentials in Kubernetes Secrets

Secure storage of credentials is a mandatory requirement for any production cluster. Never insert proxy username/password directly into YAML manifests that are stored in a Git repository.

Creating a Secret for the proxy

# Create via kubectl (values are automatically base64 encoded)
kubectl create secret generic proxy-credentials \
  --namespace=production \
  --from-literal=http-proxy-url='http://user:[email protected]:8080' \
  --from-literal=https-proxy-url='http://user:[email protected]:8080' \
  --from-literal=socks5-proxy-url='socks5://user:[email protected]:1080'

# Check the created Secret
kubectl get secret proxy-credentials -n production -o yaml

YAML manifest of the Secret

apiVersion: v1
kind: Secret
metadata:
  name: proxy-credentials
  namespace: production
  labels:
    app: proxy-config
    managed-by: ops-team
type: Opaque
# Values are encoded in base64
# echo -n 'http://user:pass@proxy:8080' | base64
stringData:
  # stringData allows specifying values in plain text
  # Kubernetes automatically encodes them in base64
  http-proxy-url: "http://user:[email protected]:8080"
  https-proxy-url: "http://user:[email protected]:8080"
  proxy-host: "gate.proxycove.com"
  proxy-port: "8080"
  proxy-username: "user"
  proxy-password: "password"

Using Secret in Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-worker
  namespace: production
spec:
  replicas: 3
  selector:
    matchLabels:
      app: api-worker
  template:
    metadata:
      labels:
        app: api-worker
    spec:
      containers:
      - name: worker
        image: mycompany/api-worker:latest
        env:
        - name: HTTP_PROXY
          valueFrom:
            secretKeyRef:
              name: proxy-credentials
              key: http-proxy-url
        - name: HTTPS_PROXY
          valueFrom:
            secretKeyRef:
              name: proxy-credentials
              key: https-proxy-url
        - name: NO_PROXY
          value: "localhost,127.0.0.1,10.0.0.0/8,.cluster.local"
        # For applications that read host/port separately
        - name: PROXY_HOST
          valueFrom:
            secretKeyRef:
              name: proxy-credentials
              key: proxy-host
        - name: PROXY_PORT
          valueFrom:
            secretKeyRef:
              name: proxy-credentials
              key: proxy-port

Integration with external secret storage

In an enterprise environment, it is recommended to use the External Secrets Operator to synchronize secrets from HashiCorp Vault, AWS Secrets Manager, or Azure Key Vault. This allows for automatic updates of proxy credentials without manual intervention and pod restarts:

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: proxy-credentials-ext
  namespace: production
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: vault-backend
    kind: ClusterSecretStore
  target:
    name: proxy-credentials
    creationPolicy: Owner
  data:
  - secretKey: http-proxy-url
    remoteRef:
      key: secret/proxy/proxycove
      property: http_proxy_url
  - secretKey: https-proxy-url
    remoteRef:
      key: secret/proxy/proxycove
      property: https_proxy_url

Diagnostics and common errors when setting up proxy in K8s

Even with the correct configuration, problems may arise. Let’s discuss the most common ones and ways to diagnose them.

Problem 1: Intra-cluster traffic goes through the proxy

Symptom: pods can no longer see each other, DNS resolution within the cluster breaks, services do not respond. The reason is that the NO_PROXY variable is not set.

The minimally required value of NO_PROXY for a standard K8s cluster:

NO_PROXY=localhost,127.0.0.1,::1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,.cluster.local,.svc,.svc.cluster.local,kubernetes.default.svc

Problem 2: Proxy is not applied to a specific pod

Diagnosis: enter the pod and check the environment variables:

# Check environment variables in the pod
kubectl exec -it <pod-name> -n production -- env | grep -i proxy

# Test connection through the proxy from the pod
kubectl exec -it <pod-name> -n production -- curl -v --proxy http://proxy:8080 https://httpbin.org/ip

# Check that the Secret is mounted correctly
kubectl exec -it <pod-name> -n production -- env | grep PROXY

Problem 3: TLS errors when using HTTPS proxy

If the proxy uses a self-signed certificate or corporate CA, applications will receive TLS verification errors. The solution is to add the proxy CA certificate to the trusted ones:

# Create ConfigMap with CA certificate
kubectl create configmap proxy-ca-cert \
  --from-file=proxy-ca.crt=./proxy-ca.crt \
  -n production

# Mounting in the pod
spec:
  containers:
  - name: app
    volumeMounts:
    - name: proxy-ca
      mountPath: /etc/ssl/certs/proxy-ca.crt
      subPath: proxy-ca.crt
  volumes:
  - name: proxy-ca
    configMap:
      name: proxy-ca-cert

Problem 4: Slow requests through the proxy

If requests through the proxy are significantly slower than direct ones β€” check:

  • DNS resolution: ensure that DNS queries are not going through the proxy (add to NO_PROXY)
  • Keep-alive connections: configure a connection pool to the proxy server
  • Geographical location: the proxy server should be physically close to the target resource
  • Type of proxy: for high-speed tasks, consider data center proxies β€” they provide minimal latency

Useful commands for diagnostics

# Check logs of Ingress Controller
kubectl logs -n ingress-nginx -l app.kubernetes.io/name=ingress-nginx --tail=100

# Check pod events
kubectl describe pod <pod-name> -n production

# Test proxy connection from the pod
kubectl run test-proxy --image=curlimages/curl --rm -it --restart=Never -- \
  curl -x http://user:pass@proxy:8080 -v https://httpbin.org/ip

# Check that the Secret exists and contains the necessary keys
kubectl get secret proxy-credentials -n production -o jsonpath='{.data}' | python3 -c "
import sys, json, base64
data = json.load(sys.stdin)
for k, v in data.items():
    print(f'{k}: {base64.b64decode(v).decode()[:20]}...')
"

Conclusion and recommendations

Setting up a proxy in Kubernetes is not a one-time task, but part of the cluster's network security architecture. Let’s summarize:

  • For NGINX Ingress Controller β€” use annotations to configure proxy parameters at the level of individual Ingress resources, and environment variables in Deployment for global settings of outgoing traffic.
  • For Traefik β€” combine static configuration through ConfigMap with Middleware for managing headers.
  • For pods β€” standard variables HTTP_PROXY / HTTPS_PROXY through Secrets is the most universal and secure approach.
  • Always configure NO_PROXY β€” otherwise, intra-cluster traffic will break.
  • Never store credentials for proxies in plain text in manifests or in Git.
  • For production β€” use External Secrets Operator for automatic rotation of credentials.

If your K8s cluster performs tasks where IP stability and minimal risk of blocks are critical β€” parsing, working with advertising APIs, geo-testing β€” we recommend using residential proxies. They provide real IPs of home users, which protection algorithms perceive as legitimate traffic. For tasks with high speed requirements and minimal risks of blocks from mobile platforms, mobile proxies are excellent β€” they operate through real 4G/5G networks of operators.