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

# Build a Next.js app on floo

> End-to-end guide. Deploy a Next.js (App Router) app, add Postgres, add per-user auth, and ship a custom domain.

This guide walks a Next.js 14+ App Router app from local code to a production URL with a database and per-user auth. Every step has runnable TypeScript code. By the end you have a working app at `https://<app>.on.getfloo.com` with a Postgres sibling service, signed-in users, and (optionally) your own domain.

If you've never deployed to floo before, read [Golden Path](/guides/golden-path) first for the minimal three-command flow.

## Before you start

You need:

* A Next.js 14+ App Router project (or a fresh `npx create-next-app@latest`).
* The project pushed to a GitHub repository. floo pulls source from GitHub — it does not upload local files.
* The floo CLI installed and authenticated (`curl -fsSL https://getfloo.com/install.sh | bash` then `floo auth login`).

## 1. Add a Dockerfile

Configure Next.js for standalone output (much smaller container):

```js next.config.js theme={null}
module.exports = {
  output: "standalone",
};
```

Then a multi-stage Dockerfile that uses it:

```dockerfile Dockerfile theme={null}
FROM node:20-slim AS deps
WORKDIR /app
COPY package.json package-lock.json* ./
RUN npm ci

FROM node:20-slim AS build
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
ENV NEXT_TELEMETRY_DISABLED=1
RUN npm run build

FROM node:20-slim AS runtime
WORKDIR /app
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
ENV PORT=3000
ENV HOSTNAME=0.0.0.0
COPY --from=build /app/public ./public
COPY --from=build /app/.next/standalone ./
COPY --from=build /app/.next/static ./.next/static
EXPOSE 3000
CMD ["node", "server.js"]
```

<Tip>
  Set `HOSTNAME=0.0.0.0`. Next.js standalone defaults to `localhost`, which Cloud Run won't reach.
</Tip>

## 2. Build-time env vars (`NEXT_PUBLIC_*`)

Any `NEXT_PUBLIC_*` variable referenced in your code is **baked into the JS bundle at build time**, not read at runtime. You must thread it through the Dockerfile as a build arg, and tell floo to pass it on every build.

```dockerfile Dockerfile theme={null}
# In the build stage:
FROM node:20-slim AS build
WORKDIR /app
ARG NEXT_PUBLIC_API_URL
ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
```

Set the value with `--build-arg`:

```bash theme={null}
floo env set NEXT_PUBLIC_API_URL=https://my-app.on.getfloo.com --app my-app --build-arg
```

Skipping this is the most common Next.js footgun on floo — your app builds, deploys, and 404s in the browser because the bundle has `undefined` baked in.

## 3. Initialize the floo config

```bash theme={null}
floo init my-nextjs-app
```

For a single-service Next.js app:

```toml floo.app.toml theme={null}
[app]
name = "my-nextjs-app"

[services.web]
type = "web"
path = "."
port = 3000
ingress = "public"
dev_command = "npm run dev"
migrate_command = "npx prisma migrate deploy"   # optional, if you use Prisma
```

## 4. Connect the repo and deploy

```bash theme={null}
git add . && git commit -m "feat: floo config + Dockerfile"
git push origin main
floo apps github connect owner/my-nextjs-app
floo deploys watch --app my-nextjs-app
```

When the deploy is green:

```bash theme={null}
floo apps status my-nextjs-app
```

<Check>
  **Your Next.js app is live** at `https://my-nextjs-app-dev.on.getfloo.com`.

  Every `git push origin main` ships to dev. `floo releases promote --app my-nextjs-app` publishes to `https://my-nextjs-app.on.getfloo.com`.
</Check>

## 5. Add a Postgres database

```bash theme={null}
floo services add postgres --app my-nextjs-app
git add .floo/services.lock && git commit -m "feat: add postgres"
git push origin main
```

`DATABASE_URL` is injected into the runtime. With Prisma:

```prisma prisma/schema.prisma theme={null}
datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}
```

`migrate_command = "npx prisma migrate deploy"` runs after every deploy and promote, keeping schema in sync.

## 6. Add per-user auth

floo manages user authentication for you. Set `access_mode = "accounts"` in `floo.app.toml`:

```toml floo.app.toml theme={null}
[app]
name = "my-nextjs-app"
access_mode = "accounts"
```

Push and deploy. From the next deploy onward, floo's gateway sits in front of your app and:

* Redirects unauthenticated requests to a hosted login page.
* Validates the session cookie on every request.
* Injects identity headers into every request that reaches your Next.js app.

Read the headers in a Server Component or Route Handler:

```tsx app/dashboard/page.tsx theme={null}
import { headers } from "next/headers";

export default async function Dashboard() {
  const h = await headers();
  const email = h.get("x-floo-user-email");
  const userId = h.get("x-floo-user-id");
  const name = h.get("x-floo-user-name");

  return (
    <main>
      <h1>Hello {name ?? email}</h1>
      <p>User id: {userId}</p>
    </main>
  );
}
```

For local development, hit your dev server with `curl -H 'X-Floo-User-Email: you@example.com'` or wrap a helper that returns a fixture user when the headers are missing.

For the full reference on access modes and identity headers, see [Add User Auth to Your App](/guides/app-auth).

## 7. Add a custom domain

```bash theme={null}
floo domains add app.example.com --app my-nextjs-app
```

Add the CNAME shown at your DNS provider. Next.js reads `Host` and `X-Forwarded-Host` from the request — no extra config needed.

## 8. Local development with prod data

```bash theme={null}
floo dev --app my-nextjs-app --service web
```

Runs `dev_command` locally with `DATABASE_URL` and other env vars sourced from your dev floo app — real Cloud SQL connection, no credentials in your shell history.

To also test signed-in flows for this accounts-mode app, add `--fixture-user`:

```bash theme={null}
floo dev --app my-nextjs-app --service web --fixture-user you@example.com
```

`floo dev` then starts a small proxy in front of each service that injects the same `X-Floo-User-*` headers floo's gateway adds in production. The output table shows both the raw service URL and the auth-proxied URL — hit the auth-proxied one for any path that reads identity headers.

## Common gotchas

* **`/healthz` is reserved.** Cloud Run's edge intercepts that exact path. Use `/health` or `/livez`.
* **`HOSTNAME=0.0.0.0`.** Next.js standalone defaults to `localhost`, which Cloud Run won't reach.
* **`NEXT_PUBLIC_*` build args.** Build-time vars must be threaded through the Dockerfile as `ARG`/`ENV` and passed via `floo env set ... --build-arg`. Otherwise the bundle has `undefined` baked in.
* **`output: "standalone"` in `next.config.js`.** The Dockerfile above assumes standalone output. Without it, the build artifact is much larger and the `COPY --from=build /app/.next/standalone` line fails.

## What's next

<CardGroup cols={2}>
  <Card title="Add User Auth — full reference" href="/guides/app-auth">
    Identity headers, access policies, and access modes in detail.
  </Card>

  <Card title="Multi-Service Routing" href="/guides/multi-service-routing">
    Deploy Next.js alongside a FastAPI or Express backend with shared origin.
  </Card>

  <Card title="Environment Variables" href="/guides/environment-variables">
    Build-time vs runtime env vars, and the `--build-arg` flag in detail.
  </Card>

  <Card title="Custom Domains" href="/guides/custom-domains">
    DNS, verification, multi-service routing.
  </Card>
</CardGroup>
