# Deploy an application

Choose hosting from the application's runtime requirements. A browser-only build
can live on a static host. Genuine request-time Forma SSR requires a running Rust
process. CI builds and tests an application; a CI job is not its permanent origin.

## Package one coherent build

Build frontend assets and FMIR first, then compile the Rust server that embeds or
loads those exact files. Keep the manifest, native modules, browser bundles and
stylesheets together. Record the application revision and preserve the lockfiles.

For a generated minimal application, the native build sequence starts with:

```sh
cd admin
npm ci
npm run build:ssr
cd ..
CARGO_BUILD_JOBS=3 cargo build --release --locked
```

The resulting binary is under `target/release/`, named by the Cargo package.
Check the application's binding configuration before moving it behind a proxy;
the minimal template intentionally listens on loopback. A container or remote
proxy usually needs a reachable interface chosen explicitly by the application.

The website's Rust origin serves both generated documentation and native routes.
Its container definition builds those two parts together. That is one practical
architecture; an application's content does not all need request-time rendering.

## Try the developer-platform packaging reference

Download the [complete developer-platform source](/downloads/forma-developer-platform-source.zip)
and inspect its [Dockerfile](/downloads/Dockerfile.txt). Extract the archive, then
open a terminal in its source root: the directory containing `Dockerfile`,
`package.json` and `native/`.

The source ZIP deliberately excludes the newspaper's protected article. Before
building, create `native/private/article.txt` with your own nonempty fictional
story (UTF-8, at most 64 KiB). Keep this file outside `dist`, public assets and
source downloads. The included `native/README.md` explains the runtime content
boundary; the container build requires this file. Then, with a working Docker
engine, run:

```sh
docker build -t getforma-site .
docker run --rm -p 8080:8080 getforma-site
```

The first command packages a local image; it does not publish a website. The
second starts that image and maps your computer's port 8080 to its HTTP port.
Leave it running while checking the site; Ctrl+C stops it and `--rm` removes that
container, while the built image remains available.

After a successful build and startup, open `http://localhost:8080`. The image is
designed to serve this website's documentation, browser playground and native lab
from one Rust origin. Its health path is `/healthz`; the native request example is
`/lab/native/?count=17`. The Dockerfile builds the frontend and matching FMIR before
embedding the native assets in the Rust binary.

For an HTTPS host, set the service environment variable
`PAYWALL_COOKIE_SECURE=true`. It enables Secure session cookies and the correct
HTTPS Origin check on newspaper forms. Leave it unset for the local HTTP command
above. This is a simulated, single-process subscription with no real accounts or
payments, not a production billing service.

The request with `count=17` should contain 17 before JavaScript runs. Clicking
**Add one** changes the current page to 18; reloading returns to the URL's 17.
That verifies request-time rendering and page-local interaction, not durable
storage. The separate Field Notes lesson demonstrates an actual save path.

**Reference deployment:** the website revision released on October 25, 2026 passed
Linux container CI and all 42 deployment acceptance checks against
[getforma.dev](https://getforma.dev/). These covered native rendering, island
activation, the newspaper's no-JavaScript forms, documentation, source downloads
and the browser playground through the HTTPS origin. This records that deployed
revision; it does not qualify a changed download or another host. Run the checklist
below against your resulting process and its HTTPS proxy, including response
headers and asset delivery.

This origin is intended for public documentation and bounded rendering demos.
It does not start the separate Field Notes inventory service. Although the source
archive includes that learning project, Field Notes remains an unauthenticated,
local-only application; its mutable inventory routes are not part of this image's
running website.

## Choose the right host shape

| Application                        | Required hosting capability                                                         |
| ---------------------------------- | ----------------------------------------------------------------------------------- |
| Static content or browser-only app | Static files, HTTPS, correct content types and deep-link handling                   |
| Rust-rendered pages and islands    | Persistent Rust process or container, HTTP port, health checks, HTTPS proxy         |
| Durable mutable data               | A storage service or durable volume, backup and migration policy, concurrency model |

GitHub Pages serves static files. GitHub Actions can build and deploy those files
or an image, but it cannot keep the Rust server alive as a public service.
An external container host can run the native origin. Keep host-specific setup
separate from Forma's rendering contract.

## Verify the deployed artifact

Before routing production traffic, check the actual built process:

1. Startup admits the expected SSR routes and finishes all typed bindings.
2. A health endpoint reports readiness only after startup succeeds.
3. A raw HTTP response contains request-specific HTML before JavaScript.
4. An island responds and retains its server-created node.
5. A JavaScript-disabled form still follows its documented fallback.
6. Invalid requests fail with the intended status and do not mutate state.
7. Every referenced hashed asset is available and has the expected content type.

Test the proxy path too. A local process succeeding does not prove that a proxy
preserves headers, serves the right origin or has access to the data volume.

## Keep response policy and caches aligned

Return `PageResponse` intact. Do not publicly cache its nonce-bearing HTML or
discard `Cache-Control: no-store`. Immutable hashed assets can be cached separately.
Retain the previous release's assets where the documented upgrade policy calls
for them; old open tabs may still request their paired files. The build's
`previousDist` setting is described in the [build reference](/docs/3.0/build/).

Use the [compatibility rules](/docs/3.0/compatibility/) for coordinated upgrades.
Prepare rollback for the application and its data schema; restoring a binary does
not automatically reverse a database migration.

## Add application operations deliberately

Forma does not provide your user authentication, tenant authorization or storage
policy merely by rendering a page. Keep server secrets outside browser assets,
apply request limits, monitor errors and protect mutable routes. Use durable
storage appropriate to your concurrency and recovery needs.

The [backend guide](/docs/3.0/backend/) explains how Axum routes and middleware,
application services and database libraries fit around Forma's page renderer.

The course's single-process local inventory example teaches validation and
persistence. Its shared JSON file is not a multi-instance database, and its
unauthenticated local interface must not be exposed as a public administration
service. Extend those boundaries before turning a learning example into a product.
