# Production-Ready: Domains, Secrets & Autoscaling

Your `hello` Rapid is live and redeploys on every push. This tutorial takes it the rest of
the way to production: a custom domain with automatic TLS, configuration and secrets injected
as environment variables (including a private connection to a managed database), and scaling
tuned for your traffic shape.

Each section shows both the dashboard and the `danube rapids` CLI — use whichever fits your
workflow. We continue with the `hello` container and team slug `acme`.

## 1. Put your own domain in front

By default your container answers at `https://hello-acme.danubedata.run`. You can map up to
**10 custom domains** per container, each with a TLS certificate provisioned automatically
via Let's Encrypt — no cert files, no renewals.

1. In the Rapid's **Domains** tab, click **Add domain** and enter `api.example.com`.
2. At your DNS provider, add the record Rapids shows you:

   | Type | Name | Value |
   |------|------|-------|
   | CNAME | `api.example.com` | `hello-acme.danubedata.run` |

3. Rapids validates the record and issues the certificate within a minute or two. Once it
   shows **Active**, `https://api.example.com` serves your container.

For an apex/root domain (`example.com`) that can't take a CNAME, use an **ALIAS**, **ANAME**,
or CNAME flattening to point at the same managed hostname. Do not pin the domain to an ingress
IP. See [Custom Domains](https://docs.danubedata.ro/custom-domains) for apex setup, migration,
and verification details.

## 2. Configuration and secrets

Rapids injects configuration as **environment variables**. Set non-secret config and secrets
(API keys, connection strings) the same way — values are stored encrypted and are never
written to the GitOps repository in plain text.

**Dashboard:** the Rapid's **Settings → Environment Variables** card.

**CLI** (variables are merged with existing ones; changing them rolls a new revision):

```bash
danube rapids update hello \
  --env NODE_ENV=production \
  --env FEATURE_SIGNUPS=on

# remove one later:
danube rapids update hello --rm-env FEATURE_SIGNUPS
```

### Talk to a managed database privately

If you run a DanubeData managed database or cache, your Rapid can reach it over **internal
cluster DNS** — the traffic never leaves the cluster, so there's no public exposure and no
egress charge. Inject the internal connection string as an env var:

```bash
danube rapids update hello \
  --env DATABASE_URL='postgres://app:••••@my-db.acme.svc.cluster.local:5432/app'
```

Read it in your app like any other env var (`process.env.DATABASE_URL`, `os.environ[...]`,
`os.Getenv(...)`). Find the internal hostname on your database's detail page.

> **Secrets are revision-scoped.** A running revision keeps the values it started with;
> updating an env var rolls a new revision with the new values. Rotate a secret by updating
> the variable — the next revision picks it up.

## 3. Scaling

Rapids scales on request load. The defaults (scale-to-zero, scale up on demand) are good for
most apps; tune these knobs when you have a specific need.

| Setting | CLI flag | What it does |
|---------|----------|--------------|
| Min replicas | `--min-scale` | `0` = scale-to-zero (default). Set `1`+ to keep instances warm and avoid cold starts. |
| Max replicas | `--max-scale` | Ceiling on instances under load. |
| Scaling metric | `--scaling-metric` | `rps` (requests/sec) or `concurrency` (in-flight requests) per pod. |
| Target | `--scaling-target` | The per-pod value of the metric Rapids aims to hold before adding a pod. |
| Request timeout | `--timeout` | Max seconds for a single request (max `3600`). |

### Scale-to-zero vs. always-warm

- **Scale-to-zero (`--min-scale 0`)** — you pay nothing while idle; the first request after
  idle pays a short cold start. Ideal for spiky or low-traffic services, internal tools, and
  webhooks.
- **Always-warm (`--min-scale 1`)** — one instance stays running so every request is fast,
  at the cost of running 24/7. Use for latency-sensitive, steady-traffic APIs.

```bash
# Latency-sensitive API: always one warm instance, scale on concurrency, longer timeout
danube rapids update hello \
  --min-scale 1 \
  --max-scale 20 \
  --scaling-metric concurrency \
  --scaling-target 50 \
  --timeout 120
```

### Picking a resource profile

Start on the **Free** profile (it includes the monthly free tier) and move up to a paid
profile or pay-per-use when you need more vCPU/memory per instance. Profiles and rates are on
the [pricing page](https://danubedata.ro/pricing).

```bash
danube rapids update hello --profile small
```

## 4. A quick production checklist

- **Listen on `$PORT`** and return `200` on a health path (e.g. `/healthz`) — Rapids only
  sends traffic to a revision once it's ready.
- **Log JSON to stdout/stderr** — logs are captured per revision and searchable in the
  dashboard. (See [Invoking Containers](https://docs.danubedata.ro/serverless-invoking).)
- **Pin immutable image tags** (commit SHA) so deploys and rollbacks are precise.
- **Keep the container stateless** — anything that must survive a scale-to-zero belongs in a
  managed database, cache, or object storage bucket, not the local filesystem.
- **Lock down access** if it isn't a public API — see
  [Authentication](https://docs.danubedata.ro/serverless-authentication).

## What's next

- **[Troubleshooting CR + Rapids](https://docs.danubedata.ro/tutorial-cr-rapids-troubleshooting)** —
  diagnose `ImagePullBackOff`, cold starts, and stuck deploys.
- **[Serverless overview](https://docs.danubedata.ro/serverless-overview)** — the full
  reference for scaling, networking, and limits.

---

**Questions?** Contact support at support@danubedata.ro
