> ## Documentation Index
> Fetch the complete documentation index at: https://docs.controlplane.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Nginx

> Deploy Nginx. A stateless reverse proxy (one replica by default, scaling configurable) that routes requests by path to workloads in the same GVC behind one public endpoint; a demo backend is deployed by default. Existing GVC; no secrets or other prerequisites needed.

## Overview

Nginx is a high-performance web server and reverse proxy. This template deploys Nginx as a stateless reverse proxy that routes incoming requests by path to other workloads in the same GVC, so several internal services can share one public endpoint. The `nginx.conf` is generated from your `locations` values and mounted into the container from a secret.

A default install also deploys a small example backend and routes every request to it, so you can confirm the proxy works before pointing it at your own services.

<Note>
  This template does not create a GVC. It deploys into an existing GVC that you already have, and every workload it proxies to must already exist in that same GVC.
</Note>

### What Gets Created

| Resource | Name | Purpose | Optional |
| - | - | - | - |
| Workload | `RELEASE_NAME-nginx` | Nginx reverse proxy on port `80` (serverless, autoscaling), open to the internet | — |
| Secret | `RELEASE_NAME-nginx-config` | Opaque secret holding the generated `nginx.conf`, mounted at `/etc/nginx/nginx.conf` | — |
| Identity | `RELEASE_NAME-nginx-identity` | Identity bound to the proxy workload | — |
| Policy | `RELEASE_NAME-nginx-policy` | Grants the identity `reveal` on the configuration secret only | — |
| Workload | `RELEASE_NAME-example` | Example backend that receives all `/` traffic; reachable only from the proxy | `enableExample: true` (the default) |

## Prerequisites

None for a default install.

To proxy to your own services, each target workload must already exist in the same GVC, and its internal firewall must allow traffic from `RELEASE_NAME-nginx` (for example `same-gvc`, or a workload list that includes the proxy).

## Installation

Install the template into an existing GVC. No `--set` values are required:

```bash theme={null}
cpln helm install RELEASE_NAME oci://ghcr.io/controlplane-com/templates/nginx \
  --version 1.1.2 --gvc GVC_NAME
```

To install using another method, follow the instructions for your preferred tool:

<CardGroup cols={2}>
  <Card title="UI" href="/template-catalog/install-manage/ui" icon="laptop">
    Browse, install, and manage templates visually
  </Card>

  <Card title="CLI" href="/template-catalog/install-manage/cli" icon="terminal">
    Manage templates from your terminal
  </Card>

  <Card title="Terraform" href="/template-catalog/install-manage/terraform" icon={<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128"><g fill-rule="evenodd"><path d="M77.941 44.5v36.836L46.324 62.918V26.082zm0 0" fill="#5c4ee5"/><path d="M81.41 81.336l31.633-18.418V26.082L81.41 44.5zm0 0" fill="#4040b2"/><path d="M11.242 42.36L42.86 60.776V23.941L11.242 5.523zm0 0M77.941 85.375L46.324 66.957v36.82l31.617 18.418zm0 0" fill="#5c4ee5"/></g></svg>}>
    Declare templates in your Terraform configurations
  </Card>

  <Card
    title="Pulumi"
    href="/template-catalog/install-manage/pulumi"
    icon={<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" id="Pulumi-Icon--Streamline-Svg-Logos" height="24" width="24">
    <desc>
        Pulumi Icon Streamline Icon: https://streamlinehq.com
    </desc>
    <path fill="#f26e7e" d="M4.683025 13.3318c0.869125 -0.5018 0.870575 -2.1264 0.003225 -3.62865s-2.27504 -2.313275 -3.1441725 -1.811475C0.672945 8.3935 0.6715 10.0181 1.53885 11.52035c0.86735 1.502275 2.27505 2.313275 3.144175 1.81145Zm0.0052 3.2167c0.86735 1.502275 0.865925 3.126875 -0.003225 3.628675 -0.86915 0.5018 -2.2768275 -0.309225 -3.144175 -1.81145 -0.8673525 -1.50225 -0.8659075 -3.126875 0.003225 -3.628675 0.8691325 -0.5018 2.276825 0.309225 3.144175 1.81145Zm5.922875 3.4243c0.86735 1.50225 0.8659 3.126775 -0.003225 3.62875 -0.869125 0.501775 -2.27685 -0.309325 -3.1442 -1.81155 -0.867325 -1.50225 -0.865875 -3.12685 0.00325 -3.628675 0.869125 -0.5018 2.276825 0.309225 3.144175 1.811475Zm-0.001925 -6.845275c0.86735 1.50225 0.8659 3.12685 -0.003225 3.628675 -0.869125 0.5018 -2.276825 -0.309225 -3.144175 -1.811475 -0.86735 -1.50225 -0.8659 -3.12685 0.003225 -3.62865 0.869125 -0.501825 2.276825 0.3092 3.144175 1.81145Z" stroke-width="0.25"></path>
    <path fill="#8a3391" d="M22.45775 11.524125c0.86725 -1.502225 0.865925 -3.12685 -0.003225 -3.62865 -0.869125 -0.501825 -2.276825 0.3092 -3.144175 1.811475 -0.86735 1.50225 -0.8659 3.126825 0.003225 3.62865 0.869125 0.501825 2.276825 -0.3092 3.144175 -1.811475Zm0.000175 3.2151c0.869075 0.5018 0.870625 2.1264 0.003225 3.62865 -0.86735 1.50225 -2.27505 2.313275 -3.144175 1.81145 -0.869125 -0.5018 -0.870575 -2.126425 -0.003225 -3.62865 0.86735 -1.50225 2.27505 -2.313275 3.144175 -1.81145ZM16.536225 18.157875c0.86915 0.501825 0.8706 2.126425 0.00325 3.628675 -0.86735 1.502125 -2.275075 2.313225 -3.1442 1.81145 -0.869125 -0.50175 -0.870575 -2.126425 -0.003225 -3.62865 0.867375 -1.502275 2.27505 -2.3133 3.144175 -1.811475Zm-0.003325 -6.843775c0.869125 0.5018 0.870575 2.126425 0.003225 3.628675s-2.27505 2.313275 -3.1442 1.811475c-0.869125 -0.501825 -0.870575 -2.126425 -0.003225 -3.628675 0.86735 -1.502275 2.27505 -2.313275 3.1442 -1.811475Z" stroke-width="0.25"></path>
    <path fill="#f7bf2a" d="M15.138225 2.06721c0 1.003615 -1.40625 1.817215 -3.14095 1.817215 -1.7347 0 -3.14095 -0.8136 -3.14095 -1.817215C8.856325 1.06359 10.262575 0.25 11.997275 0.25c1.7347 0 3.14095 0.81359 3.14095 1.81721ZM9.2166 5.482375c0 1.003625 -1.40625 1.8172 -3.14095 1.8172 -1.7347 0 -3.14095 -0.813575 -3.14095 -1.8172s1.40625 -1.817225 3.14095 -1.817225c1.7347 0 3.14095 0.8136 3.14095 1.817225Zm8.71005 1.8172c1.7347 0 3.14095 -0.813575 3.14095 -1.8172s-1.40625 -1.817225 -3.14095 -1.817225c-1.7347 0 -3.14095 0.8136 -3.14095 1.817225s1.40625 1.8172 3.14095 1.8172Zm-2.788425 1.605625c0 1.003625 -1.40625 1.8172 -3.14095 1.8172 -1.7347 0 -3.14095 -0.813575 -3.14095 -1.8172 0 -1.0036 1.40625 -1.8172 3.14095 -1.8172 1.7347 0 3.14095 0.8136 3.14095 1.8172Z" stroke-width="0.25"></path>
    </svg>}
  >
    Declare templates in your Pulumi programs
  </Card>
</CardGroup>

## Configuration

### Proxy Workload

```yaml theme={null}
proxyWorkload:
  image: nginx:1.31.4 # pinned: `latest` makes installs non-reproducible
  port: 80
  capacityAI: false
  timeoutSeconds: 5
```

* `proxyWorkload.image` — the Nginx container image.
* `proxyWorkload.port` — the container port exposed by the workload. Leave it at `80`: the generated `nginx.conf` listens on `80` and the health probes check port `80`, neither of which follows this value.
* `proxyWorkload.capacityAI` — enables Control Plane CapacityAI on the proxy workload.
* `proxyWorkload.timeoutSeconds` — the workload request timeout in seconds.

### Resources

```yaml theme={null}
resources:
  cpu: 100m
  memory: 128Mi
```

* `resources.cpu` / `resources.memory` — the CPU and memory limit for each Nginx replica.

### Autoscaling

```yaml theme={null}
autoscaling:
  minScale: 1
  maxScale: 1
  maxConcurrency: 1000
```

* `autoscaling.minScale` / `autoscaling.maxScale` — the minimum and maximum replica count. The proxy is stateless, so raising `maxScale` scales it horizontally.
* `autoscaling.maxConcurrency` — the maximum number of concurrent requests per replica.

The scaling metric is fixed in the chart: CPU, with a 50 percent target.

### Example Backend

```yaml theme={null}
enableExample: true # set to true to deploy an example backend workload and route all traffic to it
```

When `true`, the chart deploys `RELEASE_NAME-example` and routes every `/` request to it. Set it to `false` before routing to your own services; otherwise the example keeps the `/` route.

### Locations

```yaml theme={null}
locations: [] # add your own proxy targets here
  # - path: /
  #   workload: my-workload
  #   port: 8080
  #   regexModifier: ""
```

Each entry becomes one Nginx `location` block that forwards matching requests to a workload in the same GVC:

| Field | Description |
| - | - |
| `path` | Path to match, such as `/` or `/api` |
| `workload` | The bare name of the target workload in the same GVC |
| `port` | The port the target workload listens on |
| `regexModifier` | An Nginx location modifier: empty for prefix matching, `~` for a case-sensitive regex, `~*` for a case-insensitive regex |

Enter only the workload name in `workload`. The chart builds the fully qualified internal hostname itself, `WORKLOAD_NAME.GVC_NAME.cpln.local`, and uses it for both the upstream address and the `Host` header sent to the target.

Example routing two paths to two services:

```yaml theme={null}
enableExample: false

locations:
  - path: /
    workload: my-workload
    port: 8080
    regexModifier: ""
  - path: /api
    workload: my-api
    port: 3000
    regexModifier: ""
```

### Built-in Routes

Two routes are always present, whatever `locations` contains:

| Route | Response |
| - | - |
| `/health` | `200` with `{"success":true,"message":"OK"}`; used by the liveness and readiness probes |
| `/fail` | `500` with `{"success":false,"message":"Error"}` |

Any `500`, `502`, `503` or `504` from an upstream workload is replaced with the `/fail` response, so the client sees the JSON error body rather than the upstream page.

## Connecting

| What | Address | Notes |
| - | - | - |
| Public endpoint | `https://CANONICAL_ENDPOINT` | Always open to the internet. Read it from `status.canonicalEndpoint` in `cpln workload get RELEASE_NAME-nginx --gvc GVC_NAME -o yaml`. |
| Private tunnel | `cpln port-forward RELEASE_NAME-nginx 8080:80 --gvc GVC_NAME`, then `http://localhost:8080` | Useful for testing routes from your machine. |
| Other workloads in the GVC | Not reachable | The chart opens no internal access to the proxy, so other workloads cannot reach it. Use its public endpoint. |
| Credentials | None | The proxy has no login. Any authentication is up to the services behind it. |

Read the canonical endpoint:

```bash theme={null}
cpln workload get RELEASE_NAME-nginx --gvc GVC_NAME -o yaml
```

Then check the built-in health route:

```bash theme={null}
curl -s https://CANONICAL_ENDPOINT/health
```

A response of `{"success":true,"message":"OK"}` means Nginx is serving. With the example backend enabled, a request to `https://CANONICAL_ENDPOINT/` returns the example greeting page.

## Operations

### Changing the Routing

The `nginx.conf` lives in the `RELEASE_NAME-nginx-config` secret and is rebuilt from your values on every Helm upgrade. Edit `locations` (and `enableExample`) in your values file, then upgrade:

```bash theme={null}
cpln helm upgrade RELEASE_NAME oci://ghcr.io/controlplane-com/templates/nginx \
  --version 1.1.2 --gvc GVC_NAME \
  -f values.yaml
```

The values file must use the chart's nesting, exactly as the blocks in [Configuration](#configuration) show. A flat dotted key is accepted without error and silently changes nothing.

The configuration is mounted as a file, and a running replica keeps the file it started with. Force a redeployment so every replica starts with the new `nginx.conf`:

```bash theme={null}
cpln workload force-redeployment RELEASE_NAME-nginx --gvc GVC_NAME
```

Then confirm a route you changed answers on the public endpoint. Whether the upgrade alone restarts the proxy has not been verified on this template, so run the redeployment every time.

Edit routing through Helm values only. Whether a later upgrade overwrites a hand edit to the secret has not been verified, so keep your routing in your values file and re-check the secret after every upgrade.

### Upgrading From 1.1.1 or Earlier

Versions 1.1.1 and earlier defaulted `proxyWorkload.image` to `nginx:latest`. Version 1.1.2 pins it to `nginx:1.31.4`, so an upgrade that does not set the image restarts the proxy on the pinned version. The proxy holds no data, so nothing needs to be migrated. To keep a specific image, set `proxyWorkload.image` explicitly.

### Scaling and Availability

The proxy is stateless, so it scales horizontally. The shipped default runs exactly one replica (`minScale: 1`, `maxScale: 1`), which means a restart or redeployment briefly interrupts traffic. Set `autoscaling.minScale` to `2` or more for an endpoint other systems depend on, and raise `autoscaling.maxScale` to let it scale out under load.

### Troubleshooting

<AccordionGroup>
  <Accordion title="Every request returns the example page">
    **Symptom:** You added `locations`, but requests to `/` still return the example greeting page.

    **Cause:** `enableExample` is still `true`, so the example backend keeps the `/` route.

    **Fix:** Set `enableExample: false`, run a Helm upgrade, then force a redeployment as described in [Changing the Routing](#changing-the-routing).
  </Accordion>

  <Accordion title="A route returns the JSON error body">
    **Symptom:** Requests to one of your paths return `500` with `{"success":false,"message":"Error"}`.

    **Cause:** The upstream returned a `5xx` or could not be reached. The usual reasons are a `workload` value that does not match the target name exactly, a wrong `port`, or a target whose internal firewall does not allow `RELEASE_NAME-nginx`.

    **Fix:** Check that the target exists in the same GVC and listens on the configured port, put only the bare workload name in `workload`, and allow the proxy in the target's internal firewall. Read the proxy logs to see the upstream error:

    ```bash theme={null}
    cpln logs '{gvc="GVC_NAME", workload="RELEASE_NAME-nginx"}' --limit 50 --since 10m
    ```
  </Accordion>

  <Accordion title="The proxy never becomes ready after a routing change">
    **Symptom:** After an upgrade the proxy restarts repeatedly, while `cpln helm upgrade` itself reported success.

    **Cause:** `locations` is written into `nginx.conf` as-is, so a malformed entry is only caught when Nginx starts. Two entries with the same path, or a `/` entry while `enableExample` is `true`, produce a duplicate location that Nginx refuses to load.

    **Fix:** Read the startup error in the logs, correct the entry in your values, and upgrade again:

    ```bash theme={null}
    cpln logs '{gvc="GVC_NAME", workload="RELEASE_NAME-nginx"} |= "emerg"' --limit 50 --since 10m
    ```

    ```bash theme={null}
    cpln workload get-deployments RELEASE_NAME-nginx --gvc GVC_NAME -o yaml
    ```
  </Accordion>

  <Accordion title="A routing change has no effect">
    **Symptom:** The upgrade succeeded, but the proxy still routes the old way.

    **Cause:** The running replicas are still using the `nginx.conf` they started with.

    **Fix:** Force a redeployment:

    ```bash theme={null}
    cpln workload force-redeployment RELEASE_NAME-nginx --gvc GVC_NAME
    ```
  </Accordion>
</AccordionGroup>

## Important Notes

* Set `enableExample: false` before routing to your own services; the default sends all `/` traffic to the example backend.
* Put only the bare workload name in `locations[].workload`; the chart appends `.GVC_NAME.cpln.local` itself.
* Each target workload's internal firewall must allow `RELEASE_NAME-nginx`, or its route returns the JSON error body.
* The proxy is always open to the internet; there is no value to make it private.
* Leave `proxyWorkload.port` at `80`; Nginx and its health probes are fixed to that port.
* After changing routing, force a redeployment so the replicas load the new `nginx.conf`.
* The example backend image, `gcr.io/knative-samples/helloworld-go`, publishes only `:latest` and cannot be pinned; it does not affect the proxy.

## External References

<CardGroup cols={2}>
  <Card title="Nginx Documentation" icon="book" href="https://nginx.org/en/docs/">
    Official Nginx documentation
  </Card>

  <Card title="Nginx Location Directive" icon="book" href="https://nginx.org/en/docs/http/ngx_http_core_module.html#location">
    How location paths and regex modifiers are matched
  </Card>

  <Card title="Nginx Reverse Proxy Guide" icon="book" href="https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/">
    Proxying requests to upstream servers
  </Card>

  <Card title="Nginx Template" icon="github" href="https://github.com/controlplane-com/templates/tree/main/nginx">
    View the source files, default values, and chart definition
  </Card>
</CardGroup>
