Back to Blog
Tutorial

How to Deploy an Astro Application in 1 Minute

Daniel Brooks5 min read
How to Deploy an Astro Application in 1 Minute

Astro is a modern web framework designed for content-driven websites. It delivers best-in-class performance by shipping zero JavaScript to the browser by default, using its island architecture to hydrate only the interactive components that need it. Astro supports multiple UI frameworks including React, Vue, Svelte, and Solid — all in the same project.

With Out Plane, you can deploy your Astro application in under a minute — directly from your GitHub repository. This guide shows you exactly how.

What You'll Need

Before starting, make sure you have:

  • Node.js 20+ installed on your machine
  • npm (comes with Node.js)
  • A GitHub account
  • An Astro application in a GitHub repository

Don't have Node.js installed? Download it from nodejs.org:

  • Windows: Download the LTS installer from nodejs.org
  • macOS: Use brew install node or download from nodejs.org
  • Linux: Run curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash - && sudo apt install -y nodejs (Ubuntu/Debian)

Once Node.js is installed, create a new Astro project:

bash
npm create astro@latest my-astro-app
cd my-astro-app

If you don't have an Astro app yet, use our example below.

Quick Start: Sample Astro Application

Here's a minimal Astro application with SSR support. Create a new project:

bash
npm create astro@latest my-astro-app
cd my-astro-app

Install the Node.js adapter for server-side rendering:

bash
npx astro add node

This installs @astrojs/node and updates your configuration automatically. Verify your astro.config.mjs looks like this:

javascript
import { defineConfig } from "astro/config";
import node from "@astrojs/node";

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

The output: "server" setting enables SSR, and the Node adapter with mode: "standalone" creates a self-contained server you can run with Node.js.

Replace src/pages/index.astro with a simple page:

astro
---
const title = "My Astro App";
---

<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width" />
    <title>{title}</title>
  </head>
  <body>
    <main style="padding: 2rem; font-family: system-ui;">
      <h1>Hello from Astro!</h1>
      <p>Deployed on Out Plane</p>
      <p>Built at: {new Date().toISOString()}</p>
    </main>
  </body>
</html>

Add a health check API endpoint at src/pages/api/health.ts:

typescript
import type { APIRoute } from "astro";

export const GET: APIRoute = () => {
  return new Response(JSON.stringify({ status: "healthy" }), {
    status: 200,
    headers: { "Content-Type": "application/json" },
  });
};

Create a Dockerfile in the project root:

dockerfile
FROM node:20-alpine AS base

# Install dependencies
FROM base AS deps
WORKDIR /app
COPY package.json package-lock.json* ./
RUN npm ci

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

# Production image
FROM base AS runner
WORKDIR /app
ENV NODE_ENV=production

RUN addgroup --system --gid 1001 astro
RUN adduser --system --uid 1001 astro

COPY --from=builder --chown=astro:astro /app/dist ./dist
COPY --from=builder --chown=astro:astro /app/node_modules ./node_modules

USER astro

EXPOSE 4321
ENV HOST=0.0.0.0
ENV PORT=4321

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

Push this code to a GitHub repository, and you're ready to deploy.

Deploy in 3 Steps

Step 1: Connect Your Repository

  1. Go to console.outplane.com
  2. Sign in with your GitHub account
  3. Select your Astro repository from the list

Out Plane automatically detects your application and configures the build process.

Step 2: Configure Your Application

Configure the following settings in the create application form:

Build Method

Select how Out Plane should build your application:

  • Dockerfile (Recommended for SSR): Uses the multi-stage Dockerfile above for optimal image size and server-side rendering support.
  • Buildpacks: Automatically detects Node.js and runs npm run build. Works for simpler Astro applications.

For production Astro applications with SSR, Dockerfile with the Node adapter is the recommended approach.

Basic Settings

  • Port: Set to 4321
  • Branch: Select main or your preferred branch
  • Region: Choose the region closest to your users

Environment Variables (Optional)

If your application uses environment-specific configuration, add them here. Astro uses two types of environment variables:

  • Server-only: Standard variables like DATABASE_URL, API_SECRET
  • Client-exposed: Prefixed with PUBLIC_ to be available in the browser

Add variables using the Add button or Raw Edit for bulk entry:

text
DATABASE_URL=postgres://user:password@host/database
API_SECRET=your-api-secret
PUBLIC_SITE_URL=https://your-app.outplane.app

Step 3: Deploy

Click Deploy Application and watch the build process:

  1. Queued → Waiting for resources
  2. Building → Installing dependencies, running astro build
  3. Deploying → Starting your Astro application
  4. Ready → Your app is live

Once the status shows Ready, your application is live. You can find your application URL at the top of the deployment page. Click the URL to open your Astro app in a new tab. SSL is automatically configured.

Production Best Practices

SSR vs Static Output

Astro supports multiple output modes. Choose based on your needs:

  • output: "static" (default): Pre-renders all pages at build time. Best for content sites that don't need dynamic data. No server required.
  • output: "server": Server-side renders all pages on each request. Best for dynamic applications with authentication or personalized content.
  • output: "hybrid": Static by default, with opt-in SSR for specific pages using export const prerender = false. Best balance for most applications.

For static output, you can use Buildpacks without the Node adapter. For server or hybrid output, use the Dockerfile approach with @astrojs/node.

Island Architecture

Astro's island architecture lets you use interactive UI components only where needed. Components are static HTML by default and only hydrate when you add a client:* directive:

astro
---
import Counter from "../components/Counter.tsx";
import Header from "../components/Header.astro";
---

<!-- Static HTML, zero JavaScript -->
<Header />

<!-- Interactive island, hydrates on page load -->
<Counter client:load />

<!-- Interactive island, hydrates when visible -->
<Counter client:visible />

This approach sends significantly less JavaScript to the browser compared to traditional SPA frameworks.

Image Optimization

Astro includes built-in image optimization. Use the <Image /> component for automatic format conversion and resizing:

astro
---
import { Image } from "astro:assets";
import heroImage from "../assets/hero.png";
---

<Image src={heroImage} alt="Hero image" width={800} />

For remote images, configure allowed domains in astro.config.mjs:

javascript
export default defineConfig({
  image: {
    remotePatterns: [
      { protocol: "https", hostname: "your-cdn.com" },
    ],
  },
});

Content Collections

Use Astro's content collections for type-safe content management. Define a collection schema in src/content/config.ts:

typescript
import { defineCollection, z } from "astro:content";

const blog = defineCollection({
  type: "content",
  schema: z.object({
    title: z.string(),
    date: z.date(),
    description: z.string(),
    draft: z.boolean().default(false),
  }),
});

export const collections = { blog };

Content collections provide built-in validation, TypeScript types, and query APIs for your Markdown and MDX content.

View Transitions

Astro supports the View Transitions API for smooth page transitions without client-side JavaScript overhead:

astro
---
import { ViewTransitions } from "astro:transitions";
---

<html>
  <head>
    <ViewTransitions />
  </head>
  <body>
    <slot />
  </body>
</html>

This gives your multi-page application a single-page app feel while maintaining the performance benefits of server-rendered HTML.

Connecting a Database

Most dynamic Astro applications need a database. Out Plane provides managed PostgreSQL:

  1. Go to Databases in the sidebar
  2. Click Create Database
  3. Select PostgreSQL version and region
  4. Copy the connection URL

Add the connection URL as an environment variable:

text
DATABASE_URL=postgres://user:password@host/database

Use it in your Astro application with Drizzle ORM:

typescript
// src/lib/db.ts
import { drizzle } from "drizzle-orm/node-postgres";
import pg from "pg";

const pool = new pg.Pool({
  connectionString: import.meta.env.DATABASE_URL,
});

export const db = drizzle(pool);

Define a schema and query it in your pages:

typescript
// src/lib/schema.ts
import { pgTable, serial, text, timestamp } from "drizzle-orm/pg-core";

export const posts = pgTable("posts", {
  id: serial("id").primaryKey(),
  title: text("title").notNull(),
  content: text("content").notNull(),
  createdAt: timestamp("created_at").defaultNow(),
});
astro
---
// src/pages/posts.astro
import { db } from "../lib/db";
import { posts } from "../lib/schema";

const allPosts = await db.select().from(posts);
---

<ul>
  {allPosts.map((post) => (
    <li>{post.title}</li>
  ))}
</ul>

Custom Domain Setup

Replace the default .outplane.app URL with your own domain:

  1. Navigate to Domains
  2. Click Map Domain
  3. Enter your domain (e.g., app.yourdomain.com)
  4. Add the DNS records shown to your domain registrar

Update your PUBLIC_SITE_URL environment variable to reflect the new domain. SSL certificates are automatically provisioned once DNS propagates.

Monitoring Your Application

After deployment, monitor your Astro application:

  • Logs: View real-time application logs including server-side rendering logs
  • Metrics: Track CPU, memory, and network usage
  • HTTP Logs: Analyze incoming requests, response times, and status codes

Access these from the sidebar in your application dashboard.

Troubleshooting

Adapter Not Configured

Install the Node adapter. SSR deployments require @astrojs/node. Run npx astro add node and verify your astro.config.mjs includes the adapter:

javascript
import node from "@astrojs/node";

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

Without the adapter, astro build will not produce a server entry point and the container will fail to start.

Static vs Server Output Confusion

Check your output mode. If you set output: "static" but use server-only features like API endpoints with POST handlers or Astro.cookies, the build will fail. Switch to output: "server" or output: "hybrid" for dynamic features.

Conversely, if your site is fully static, remove the Node adapter and use output: "static" with Buildpacks for simpler deployments.

Environment Variables Not Accessible

Use import.meta.env instead of process.env. Astro uses Vite under the hood. Access environment variables with:

typescript
// Server-side (available in .astro files and API routes)
const dbUrl = import.meta.env.DATABASE_URL;

// Client-side (must be prefixed with PUBLIC_)
const siteUrl = import.meta.env.PUBLIC_SITE_URL;

Variables without the PUBLIC_ prefix are only available on the server.

Build Errors with Dependencies

Check Node.js compatibility. Some npm packages require specific Node.js versions. Verify your package.json includes an engines field:

json
{
  "engines": {
    "node": ">=20.0.0"
  }
}

If using native dependencies, make sure your Dockerfile uses a compatible base image like node:20-alpine with libc6-compat.

Hydration Mismatch Errors

Ensure client components receive serializable props. When using client:load or client:visible directives, all props passed to the component must be serializable (strings, numbers, booleans, plain objects). Functions, Dates, and class instances cannot be passed as props to client-side islands.

astro
---
import Counter from "../components/Counter.tsx";
---

<!-- Good: serializable prop -->
<Counter initialCount={5} client:load />

<!-- Bad: non-serializable prop -->
<Counter onCount={() => console.log("clicked")} client:load />

Next Steps

Your Astro application is now deployed and running in production. Here's what to explore next:

  • Scale your application: Adjust instance types and auto-scaling settings for higher traffic
  • Set up CI/CD: Enable automatic deployments on every git push
  • Add monitoring: Integrate with external monitoring tools via Out Plane's metrics export
  • Add integrations: Install Astro integrations for Tailwind, MDX, Sitemap, and more from the Astro ecosystem

Summary

Deploying an Astro application to Out Plane takes three steps:

  1. Connect your GitHub repository
  2. Configure port (4321), environment variables, and build method
  3. Deploy and get your live URL with automatic HTTPS

No server configuration, no manual SSL setup, no infrastructure management. Full support for server-side rendering, island architecture, and content collections.

Ready to deploy your Astro application? Get started with Out Plane and receive $20 in free credit.


Tags

astro
deployment
tutorial
javascript
typescript
static-site

Start deploying in minutes

Connect your GitHub repository and deploy your first application today. $20 free credit. No credit card required.