# Deploying to App Platform

> What the deployment plan contains, how a release is verified, and what nextship refuses to touch.

Source: https://nextship.doodlebytestudio.in/docs/deploying

```bash
nextship deploy        # print the plan
nextship deploy --yes  # execute it
```

## Options

| Option | Default | Meaning |
|---|---|---|
| `--yes` | off | Execute the plan. Without it, `deploy` only prints |
| `--region <slug>` | `blr` | App Platform region. Recorded on the first deploy and reused after that |
| `--size <slug>` | `apps-s-1vcpu-0.5gb` | Instance size |
| `--registry <name>` | the app name | Registry name, which must be unique across all of DigitalOcean |

## The plan comes first, every time

> **Note:** **Nothing is created, changed or charged until `--yes`.** The plan names the app, the
> region, the instance size, the registry and the image tag, so the thing you approve is the
> thing that happens.

## The image tag is derived from the build

The tag is the deployment id, computed from the content of the build: the nextship
version, the generated Dockerfile, the adapter and prune script, the Server Actions key,
and the name and contents of every env file.

Change any of them and the tag changes. That is what stops an edited env file from
shipping under a tag that already exists.

When the working tree is not a clean commit, the id is unique to that build instead, and
the CLI says so rather than implying a reproducibility it cannot provide:

```
  deployment dpl-2b2c3e535f1e-ebee628b
  this id is unique to this build, because the source is not a clean commit
```

## Your settings survive

App Platform replaces the **whole** app spec on update. A naive deploy would silently drop
anything nextship does not know about.

So nextship merges instead of overwriting. Ingress rules, components you added by hand,
and environment variables set in the control panel are all carried forward. This was found
by checking a real spec before writing to it: an `ingress` block would have been lost.

## It waits for the app to actually serve

A release is not finished when the API accepts it. nextship polls until the deployment
reports that it is live, then prints the URL. It stops waiting after 15 minutes and says
so; the deployment is not cancelled and may still finish, so the control panel has the
final word.

The URL it prints is the platform host, not the field the API calls `live_url`. That field
becomes your primary custom domain the moment you attach one, which means it points at a
name that does not resolve until you have created the DNS record.

## Pages the CDN keeps

App Platform serves your app through Cloudflare, and Cloudflare honours the caching Next.js
asks for. For a page with no revalidate time, Next.js sends `Cache-Control:
s-maxage=31536000`, a year. So when a Server Action calls `revalidatePath` or
`revalidateTag`, the container renders the new page, but visitors can keep getting the copy
the edge already holds. It works in local testing, because nothing sits in front of the
container there.

Give every page you revalidate on demand a revalidate time:

```tsx title="app/posts/page.tsx"
export const revalidate = 30
```

The page then sends `s-maxage=30`, and the edge keeps it at most that long. Our own
end-to-end test on macOS measured the change reaching visitors in about 30 seconds this
way. `nextship doctor` warns when your source revalidates on demand.

## What it will not do

It only ever touches the app recorded in `nextship.json`. It creates nothing else, and it
deletes nothing at all. If the recorded app is missing, it says so and stops rather than
creating a replacement that would leave the original orphaned and billing.

## One change at a time

`deploy`, `env push`, `env rm`, `domain add` and `domain rm` refuse while the app has a
deployment still in progress, because a second change would replace the first part way
through. `deploy` checks before it builds, so it says so immediately rather than after a
full build and push.

`rollback` is the exception. It is how you get away from a bad deployment, including one
still rolling out, so whether it is possible is left to App Platform's own rollback
validation.
