Skip to main content
This guide walks you through building and deploying a Next.js application to Magic Containers with GitHub Container Registry. You’ll need:
  • A GitHub account for source code and container registry
  • A bunny.net account with Magic Containers enabled

Create the Next.js app

Create a new Next.js project:
npx create-next-app@latest app-nextjs
cd app-nextjs

Configure standalone output

Update your next.config.ts to enable standalone output mode for Docker:
next.config.ts
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  output: "standalone",
};

export default nextConfig;

Create an API route

Create a simple API route to test the deployment:
app/api/route.ts
import { NextResponse } from "next/server";

export async function GET() {
  return NextResponse.json({ message: "Hello from Bunny 🐰" });
}

Run locally

Start the development server:
npm run dev
Visit http://localhost:3000 in your browser, or test the API route:
curl http://localhost:3000/api

Create the Dockerfile

Dockerfile
FROM node:22-alpine AS base

FROM base AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci

FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build

FROM base AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
RUN if [ -d "/app/public" ]; then cp -r /app/public ./public; fi

ENV PORT=80
EXPOSE 80

CMD ["node", "server.js"]

Build and push to GitHub Container Registry

Create .github/workflows/build.yml to automatically build and push on every commit to main:
.github/workflows/build.yml
name: Build and Push

on:
  push:
    branches: [main]

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write

    steps:
      - uses: actions/checkout@v4

      - name: Log in to GitHub Container Registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
Push your code to trigger the workflow:
git init
git add .
git commit -m "Initial commit"
git remote add origin https://github.com/YOUR_USERNAME/app-nextjs.git
git push -u origin main
If your package is private, set the visibility to Public in GitHub or configure Magic Containers with registry credentials.

Deploy to Magic Containers

1

Create a new app

In the bunny.net dashboard, go to Magic Containers and click Add App. Enter a name and select your deployment option.
2

Add a container

Click Add Container, then configure:
FieldValue
RegistryGitHub Container Registry
ImageYOUR_USERNAME/{imageName}
Taglatest
3

Add an endpoint

Go to the Endpoints tab, click Add New Endpoint, and set the container port to 80.
4

Deploy

Click Add Container, then Next Step, and Confirm and Create.
For more details, see the quickstart guide.

Test your app

Visit your container URL in the browser to see your Next.js site, or test the API route:
curl https://mc-xxx.bunny.run/api
Response
{ "message": "Hello from Bunny 🐰" }
You can add a custom hostname from the Endpoints section in your app settings.

Connect a database

You can connect your app to Bunny Database directly from the dashboard:
  1. Go to Database > [Your Database] > Access
  2. Click Generate Tokens
  3. Click Add Secrets to Magic Container App
  4. Select your app
The DB_URL and DB_TOKEN environment variables are now available in your app:
app/api/users/route.ts
import { NextResponse } from "next/server";
import { createClient } from "@libsql/client/web";

const client = createClient({
  url: process.env.DB_URL!,
  authToken: process.env.DB_TOKEN!,
});

export async function GET() {
  const result = await client.execute("SELECT * FROM users");

  return NextResponse.json(result.rows);
}
See the TypeScript SDK documentation for more details.

Next steps

  1. Automate deploys with GitHub Actions
  2. Add a custom hostname
  3. Add a persistent volume