Skip to main content
This guide walks you through building and deploying an Astro 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 Astro app

Create a new Astro project:
npm create astro@latest app-astro
cd app-astro

Add the Node adapter

Astro requires an adapter for server-side rendering. Install the Node adapter:
npx astro add node
This updates your astro.config.mjs to include the adapter:
astro.config.mjs
import { defineConfig } from "astro/config";
import node from "@astrojs/node";

export default defineConfig({
  output: "server",
  adapter: node({
    mode: "standalone",
  }),
});

Create an API route

Create a simple endpoint to test the deployment:
src/pages/index.json.ts
import type { APIRoute } from "astro";

export const GET: APIRoute = () => {
  return new Response(JSON.stringify({ message: "Hello from Bunny 🐰" }));
};

Run locally

Start the development server:
npm run dev
Visit http://localhost:4321 in your browser, or test the API route:
curl http://localhost:4321/index.json

Create the Dockerfile

Dockerfile
FROM node:22-alpine AS base
WORKDIR /app
COPY package*.json ./

FROM base AS deps
RUN npm install

FROM deps AS build
COPY . .
RUN npm run build

FROM base AS runtime
COPY package*.json ./
RUN npm install --omit=dev
COPY --from=build /app/dist ./dist

ENV HOST=0.0.0.0
ENV PORT=80
EXPOSE 80

CMD ["node", "./dist/server/entry.mjs"]

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-astro.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 Astro site, or test the API route:
curl https://mc-xxx.bunny.run/index.json
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:
src/pages/api/users.ts
import type { APIRoute } from "astro";
import { createClient } from "@libsql/client/web";

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

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

  return new Response(JSON.stringify(result.rows), {
    status: 200,
    headers: {
      "Content-Type": "application/json",
    },
  });
};
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