Skip to content

Deploying an Astro App

Astro is a modern web framework for building fast, content-focused websites with an island architecture that ships zero JavaScript by default. It combines the best of static site generation with optional server-side rendering (SSR), partial hydration through islands, and support for multiple JavaScript frameworks (React, Vue, Svelte, etc.) within the same project. Astro excels at creating high-performance websites, blogs, documentation sites, and content-driven applications.

This comprehensive guide walks through deploying an Astro application to Klutch.sh using either Nixpacks (automatic zero-configuration deployment) or a Dockerfile (manual container control). You’ll learn how to create and structure an Astro project, build components and pages, configure environment variables, implement security best practices, set up monitoring, deploy custom domains, and troubleshoot common issues. By the end of this guide, you’ll have a production-ready Astro application running on Klutch.sh’s global infrastructure with automatic HTTPS and optimized performance.

Prerequisites

  • Node.js & npm (version 18+) – Download Node.js
  • Git installed locally and a GitHub account (Klutch.sh uses GitHub as the only git source)
  • Klutch.sh account with access to the dashboard at klutch.sh/app
  • Basic knowledge of HTML, CSS, JavaScript/TypeScript, and the Node.js ecosystem

Getting Started: Create an Astro App

1. Create a New Astro Project

Initialize a new Astro project using the interactive scaffolding tool:

Terminal window
npm create astro@latest my-astro-app
cd my-astro-app
npm install

The scaffolding tool will ask you several questions about your project setup (framework integrations, strict TypeScript, etc.). Select the defaults or customize based on your needs.

2. Project Structure

A typical Astro project structure looks like:

my-astro-app/
├── src/
│ ├── components/
│ │ ├── Header.astro
│ │ ├── Footer.astro
│ │ └── ...
│ ├── layouts/
│ │ └── Layout.astro
│ ├── pages/
│ │ ├── index.astro
│ │ ├── about.astro
│ │ ├── blog/
│ │ │ ├── index.astro
│ │ │ └── [slug].astro
│ │ └── ...
│ ├── styles/
│ │ └── global.css
│ └── ...
├── public/
│ ├── favicon.svg
│ └── ...
├── astro.config.mjs
├── package.json
├── tsconfig.json
├── Dockerfile
└── README.md

3. Run the Development Server

Start the Astro development server:

Terminal window
npm run dev

Navigate to http://localhost:4321 in your browser. The site will automatically reload as you make changes.

4. Sample Layout Component

Create a base layout for your pages:

src/layouts/Layout.astro
---
interface Props {
title: string;
description?: string;
}
const { title, description } = Astro.props;
---
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content={description || "A website built with Astro"} />
<title>{title}</title>
<style is:global>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: system-ui, -apple-system, sans-serif;
line-height: 1.6;
color: #333;
background-color: #f5f5f5;
}
header {
background-color: #1a1a1a;
color: white;
padding: 1rem 0;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
main {
max-width: 1200px;
margin: 2rem auto;
padding: 0 1rem;
min-height: calc(100vh - 300px);
}
footer {
background-color: #1a1a1a;
color: white;
text-align: center;
padding: 2rem 0;
margin-top: 3rem;
}
</style>
</head>
<body>
<header>
<nav style="max-width: 1200px; margin: 0 auto; padding: 0 1rem;">
<h1 style="margin-bottom: 0.5rem;">My Astro Site</h1>
<ul style="list-style: none; display: flex; gap: 2rem;">
<li><a href="/" style="color: white; text-decoration: none;">Home</a></li>
<li><a href="/about" style="color: white; text-decoration: none;">About</a></li>
<li><a href="/blog" style="color: white; text-decoration: none;">Blog</a></li>
</ul>
</nav>
</header>
<main>
<slot />
</main>
<footer>
<p>&copy; 2024 My Astro Site. All rights reserved.</p>
</footer>
</body>
</html>

5. Sample Home Page

Create the home page:

src/pages/index.astro
---
import Layout from "../layouts/Layout.astro";
---
<Layout title="Home | My Astro Site" description="Welcome to my Astro website">
<h1>Welcome to My Astro Site</h1>
<p>
This is a fast, modern website built with Astro and deployed on Klutch.sh.
</p>
<p>
Astro ships zero JavaScript by default, making your site incredibly fast.
</p>
<h2 style="margin-top: 2rem;">Features</h2>
<ul>
<li>⚡ Lightning-fast performance</li>
<li>🎨 Beautiful, responsive design</li>
<li>📦 Zero JavaScript by default</li>
<li>🚀 Easy deployment with Klutch.sh</li>
</ul>
</Layout>

6. Sample Blog Page

Create a blog post template with dynamic routing:

src/pages/blog/[slug].astro
---
import Layout from "../../layouts/Layout.astro";
// In production, fetch posts from your CMS or API
const posts = [
{
slug: "first-post",
title: "My First Blog Post",
date: new Date("2024-01-15"),
content: "This is the content of my first blog post.",
},
{
slug: "second-post",
title: "Another Great Post",
date: new Date("2024-02-20"),
content: "More interesting content here.",
},
];
export function getStaticPaths() {
return posts.map((post) => ({
params: { slug: post.slug },
props: { post },
}));
}
const { post } = Astro.props;
---
<Layout title={`${post.title} | Blog`} description={post.title}>
<article>
<h1>{post.title}</h1>
<p style="color: #666; font-size: 0.9rem;">
Posted on {post.date.toLocaleDateString()}
</p>
<p>{post.content}</p>
</article>
<nav style="margin-top: 2rem;">
<a href="/blog">← Back to Blog</a>
</nav>
</Layout>

7. Sample API Integration

Create a utility file for API calls:

src/lib/api.ts
const API_BASE_URL = import.meta.env.PUBLIC_API_URL || "https://api.example.com";
export interface Article {
id: string;
title: string;
slug: string;
content: string;
author: string;
publishedAt: string;
}
export async function getArticles(): Promise<Article[]> {
try {
const response = await fetch(`${API_BASE_URL}/articles`, {
headers: {
"Content-Type": "application/json",
},
});
if (!response.ok) {
throw new Error(`API error: ${response.statusText}`);
}
return await response.json();
} catch (error) {
console.error("Failed to fetch articles:", error);
return [];
}
}
export async function getArticleBySlug(slug: string): Promise<Article | null> {
try {
const response = await fetch(`${API_BASE_URL}/articles/${slug}`, {
headers: {
"Content-Type": "application/json",
},
});
if (!response.ok) {
return null;
}
return await response.json();
} catch (error) {
console.error(`Failed to fetch article ${slug}:`, error);
return null;
}
}
export async function createArticle(data: Omit<Article, "id" | "publishedAt">): Promise<Article | null> {
try {
const response = await fetch(`${API_BASE_URL}/articles`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
});
if (!response.ok) {
throw new Error(`API error: ${response.statusText}`);
}
return await response.json();
} catch (error) {
console.error("Failed to create article:", error);
return null;
}
}

8. Configure Environment Variables

Create an .env file for development:

Terminal window
PUBLIC_API_URL=http://localhost:3000/api
PUBLIC_SITE_NAME=My Astro Site

Access these in your Astro files:

---
const siteName = import.meta.env.PUBLIC_SITE_NAME;
const apiUrl = import.meta.env.PUBLIC_API_URL;
---
<h1>{siteName}</h1>

Local Production Build Test

Before deploying, test the production build locally:

Terminal window
npm run build
npm run preview

Visit http://localhost:4321 to verify that your site renders correctly in production mode.


Deploying with Nixpacks

Nixpacks automatically detects your Node.js/Astro application and configures build and runtime environments without requiring a Dockerfile. This is the simplest deployment method for Astro applications.

Prerequisites for Nixpacks Deployment

  • Your Astro project pushed to a GitHub repository
  • Valid package.json with build and start scripts
  • No Dockerfile in the repository root (if one exists, Klutch.sh will use Docker instead)

Steps to Deploy with Nixpacks

  1. Push Your Astro Project to GitHub

    Initialize and push your project to GitHub if you haven’t already:

    Terminal window
    git init
    git add .
    git commit -m "Initial Astro app"
    git branch -M main
    git remote add origin git@github.com:YOUR_USERNAME/YOUR_REPO.git
    git push -u origin main
  2. Log In to Klutch.sh Dashboard

    Go to klutch.sh/app and sign in with your GitHub account.

  3. Create a Project

    Navigate to the Projects section and create a new project for your Astro app.

  4. Create an App

    Click “Create App” and select your GitHub repository.

  5. Select the Branch

    Choose the branch you want to deploy (typically main).

  6. Configure Traffic Type

    Select HTTP as the traffic type for Astro (a web framework serving HTML/assets).

  7. Set the Internal Port

    Set the internal port to 4321 – this is the port where Astro’s development server listens and should be used for production builds with a Node.js server.

  8. Add Environment Variables (Optional)

    Add any environment variables your Astro app requires:

    NODE_ENV=production
    PUBLIC_API_URL=https://api.example.com
    PUBLIC_SITE_NAME=My Awesome Site

    If you need to customize the Nixpacks build or start command, use these environment variables:

    • BUILD_COMMAND: Override the default build command (e.g., npm run build)
    • START_COMMAND: Override the default start command (e.g., npm run preview)
  9. Configure Compute Resources

    Select your region, compute size, and number of instances based on expected traffic.

  10. Deploy

    Click “Create” to start the deployment. Nixpacks will automatically build and deploy your Astro app. Your app will be available at a URL like https://example-app.klutch.sh.


Deploying with Docker

For more control over your deployment environment, you can use a Dockerfile. Klutch.sh automatically detects a Dockerfile in your repository root and uses it for deployment.

Creating a Dockerfile for Astro

Create a Dockerfile in the root of your Astro project:

# === Build stage ===
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# === Runtime stage ===
FROM node:20-alpine AS runtime
WORKDIR /app
RUN npm install -g serve
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package*.json ./
ENV NODE_ENV=production
ENV PORT=4321
EXPOSE 4321
CMD ["npm", "run", "preview"]

Dockerfile Notes

  • Builder stage: Installs dependencies and builds the static Astro site into the dist folder.
  • Runtime stage: Uses a lightweight Node.js Alpine image with the serve utility to serve the compiled Astro site.
  • Port: The PORT environment variable is set to 4321, which is the recommended internal port for Astro.
  • Multi-stage build: Reduces final image size by excluding build tools and dev dependencies from the runtime container.

Alternative Dockerfile for Static Hosting (Nginx)

If you prefer to serve your Astro static site with Nginx (very lightweight):

# === Build stage ===
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# === Runtime stage (Nginx) ===
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

Note: With the Nginx Dockerfile, set the internal port to 80 instead of 4321.

Steps to Deploy with Docker

  1. Create a Dockerfile

    Add the Dockerfile (shown above) to the root of your Astro repository.

  2. Test Locally (Optional)

    Build and test the Docker image locally:

    Terminal window
    docker build -t astro-app:latest .
    docker run -p 4321:4321 astro-app:latest

    Visit http://localhost:4321 to verify.

  3. Push to GitHub

    Commit and push the Dockerfile and your code:

    Terminal window
    git add Dockerfile
    git commit -m "Add Dockerfile for production deployment"
    git push origin main
  4. Create an App in Klutch.sh

    Go to klutch.sh/app, navigate to “Create App”, and select your repository.

  5. Configure the App
    • Traffic Type: Select HTTP
    • Internal Port: Set to 4321 (or 80 if using the Nginx Dockerfile)
    • Environment Variables: Add any required runtime variables
  6. Deploy

    Klutch.sh automatically detects the Dockerfile and uses it to build and deploy your app. Your app will be available at https://example-app.klutch.sh.


Environment Variables

Define all environment variables in the Klutch.sh dashboard. Here’s a recommended set for production:

NODE_ENV=production
PUBLIC_API_URL=https://api.example.com
PUBLIC_SITE_NAME=My Awesome Astro Site
PUBLIC_ANALYTICS_ID=your-analytics-key

Accessing Environment Variables in Astro

Access environment variables in your Astro files. Variables prefixed with PUBLIC_ are exposed to the browser:

src/pages/index.astro
---
const siteName = import.meta.env.PUBLIC_SITE_NAME;
const apiUrl = import.meta.env.PUBLIC_API_URL;
---
<h1>{siteName}</h1>
<p>API: {apiUrl}</p>

In server-side code (like API endpoints), you can use all environment variables:

src/pages/api/data.json.ts
export async function GET() {
const apiSecret = import.meta.env.API_SECRET; // Only available on server
return new Response(
JSON.stringify({ message: "Server-side data" }),
{ headers: { "Content-Type": "application/json" } }
);
}

Persistent Storage

If your Astro application needs to store files or user-generated content, you can use persistent volumes in Klutch.sh.

Adding Persistent Volumes

  1. In the Klutch.sh dashboard, go to your app’s Volumes section.
  2. Click Add Volume.
  3. Set the mount path (e.g., /data, /uploads, or /var/www/uploads).
  4. Set the size (e.g., 1 GiB, 10 GiB).
  5. Save and redeploy your app.

Example: Using Persistent Storage for User Uploads

src/pages/api/upload.ts
---
export async function POST({ request }: { request: Request }) {
try {
const formData = await request.formData();
const file = formData.get("file") as File;
if (!file) {
return new Response(
JSON.stringify({ error: "No file provided" }),
{ status: 400, headers: { "Content-Type": "application/json" } }
);
}
// Save to persistent volume mounted at /uploads
const filePath = `/uploads/${file.name}`;
const buffer = await file.arrayBuffer();
// In production, use Node.js fs module to write the file
// const fs = require("fs");
// fs.writeFileSync(filePath, Buffer.from(buffer));
return new Response(
JSON.stringify({ success: true, path: filePath }),
{ headers: { "Content-Type": "application/json" } }
);
} catch (error) {
return new Response(
JSON.stringify({ error: "Upload failed" }),
{ status: 500, headers: { "Content-Type": "application/json" } }
);
}
}
---

Security Best Practices

1. HTTPS/SSL Enforcement

Klutch.sh automatically provides HTTPS for all deployed apps. Ensure your Astro app redirects HTTP to HTTPS:

src/middleware.ts
---
import { defineMiddleware } from "astro:middleware";
export const onRequest = defineMiddleware((context, next) => {
const url = new URL(context.request.url);
// Redirect HTTP to HTTPS in production
if (
import.meta.env.PROD &&
url.protocol === "http:" &&
!url.hostname.includes("localhost")
) {
return new Response(null, {
status: 301,
headers: {
Location: `https://${url.hostname}${url.pathname}${url.search}`,
},
});
}
return next();
});

2. Content Security Policy

Add security headers to your Astro app:

src/middleware.ts
---
import { defineMiddleware } from "astro:middleware";
export const onRequest = defineMiddleware((context, next) => {
const response = next();
response.headers.set("X-Content-Type-Options", "nosniff");
response.headers.set("X-Frame-Options", "DENY");
response.headers.set("X-XSS-Protection", "1; mode=block");
response.headers.set(
"Content-Security-Policy",
"default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';"
);
return response;
});

3. Environment Variable Protection

Never commit sensitive data to version control. Use Klutch.sh environment variables for:

API_KEY=your-secret-api-key
DATABASE_PASSWORD=your-db-password
JWT_SECRET=your-jwt-secret

Access these securely in server-side code only:

src/lib/api.ts
const apiKey = import.meta.env.API_KEY; // Only available on server if not prefixed with PUBLIC_

4. Input Validation

Validate and sanitize all user inputs:

src/pages/api/contact.ts
---
import { z } from "astro/zod";
const ContactSchema = z.object({
name: z.string().min(1).max(100),
email: z.string().email(),
message: z.string().min(1).max(5000),
});
export async function POST({ request }: { request: Request }) {
try {
const data = await request.json();
const validData = ContactSchema.parse(data);
// Process valid data
return new Response(
JSON.stringify({ success: true, message: "Contact form submitted" }),
{ headers: { "Content-Type": "application/json" } }
);
} catch (error) {
return new Response(
JSON.stringify({ error: "Invalid form data" }),
{ status: 400, headers: { "Content-Type": "application/json" } }
);
}
}
---

5. CORS Configuration

Configure CORS for API endpoints if needed:

src/pages/api/data.json.ts
export async function GET({ request }: { request: Request }) {
const origin = request.headers.get("origin");
const allowedOrigins = [
"https://example-app.klutch.sh",
"https://custom-domain.com",
];
const response = new Response(
JSON.stringify({ data: "Your API response" }),
{ headers: { "Content-Type": "application/json" } }
);
if (allowedOrigins.includes(origin || "")) {
response.headers.set("Access-Control-Allow-Origin", origin || "");
}
return response;
}

6. Dependency Security

Regularly audit and update dependencies:

Terminal window
npm audit
npm audit fix
npm update

7. Secure Deployment Configuration

Ensure your astro.config.mjs is configured securely:

astro.config.mjs
import { defineConfig } from "astro/config";
export default defineConfig({
// Build output directory
outDir: "./dist",
// Public base path
base: "/",
// Server configuration
server: {
host: "0.0.0.0",
port: 4321,
},
// Security headers
integrations: [],
// Enable strict mode
vite: {
ssr: {
external: ["node-fetch"],
},
},
});

Monitoring and Logging

Health Check Endpoint

Create a health check endpoint for monitoring:

src/pages/api/health.json.ts
---
export async function GET() {
return new Response(
JSON.stringify({
status: "healthy",
timestamp: new Date().toISOString(),
uptime: process.uptime ? process.uptime() : "N/A",
}),
{ headers: { "Content-Type": "application/json" } }
);
}
---

Client-Side Error Tracking

Implement error tracking for client-side issues:

src/layouts/Layout.astro
---
const enableErrorTracking = import.meta.env.PUBLIC_ERROR_TRACKING === "true";
---
<html>
<head>
<!-- Error tracking script -->
{enableErrorTracking && (
<script>
window.addEventListener("error", (event) => {
// Send error to monitoring service
fetch("/api/errors", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
message: event.error?.message,
stack: event.error?.stack,
timestamp: new Date().toISOString(),
}),
}).catch(() => {
// Silently fail if error tracking is unavailable
});
});
</script>
)}
</head>
<!-- ... rest of layout ... -->
</html>

Server-Side Logging

Log important events on the server:

src/lib/logger.ts
interface LogEntry {
timestamp: string;
level: "info" | "warn" | "error";
message: string;
data?: Record<string, any>;
}
export function log(level: string, message: string, data?: Record<string, any>) {
const entry: LogEntry = {
timestamp: new Date().toISOString(),
level: level as any,
message,
data,
};
console.log(JSON.stringify(entry));
}
export const logger = {
info: (msg: string, data?: any) => log("info", msg, data),
warn: (msg: string, data?: any) => log("warn", msg, data),
error: (msg: string, data?: any) => log("error", msg, data),
};

Custom Domains

To use a custom domain with your Klutch.sh-deployed Astro app:

1. Add the Domain in Klutch.sh

In the Klutch.sh dashboard, go to your app’s settings and add your custom domain (e.g., blog.example.com).

2. Update Your DNS Provider

Update your DNS records with the CNAME provided by Klutch.sh:

CNAME: blog.example.com → example-app.klutch.sh

3. Wait for DNS Propagation

DNS changes can take up to 48 hours to propagate. Use tools to verify:

Terminal window
nslookup blog.example.com
# or
dig blog.example.com

Once propagated, your Astro app will be accessible at your custom domain with automatic HTTPS.


Troubleshooting

Issue 1: Build Fails with Memory Error

Error: npm run build fails with “JavaScript heap out of memory”

Solutions:

  • Increase Node memory: NODE_OPTIONS=--max-old-space-size=4096 npm run build
  • Set BUILD_COMMAND in Klutch.sh: NODE_OPTIONS=--max-old-space-size=4096 npm run build
  • Optimize your Astro code and reduce bundle size
  • Consider splitting large pages or reducing image sizes

Issue 2: Port Already in Use

Error: EADDRINUSE: address already in use :::4321

Solutions:

  • Ensure the internal port in Klutch.sh matches your Astro configuration
  • Kill the process using the port: lsof -ti:4321 | xargs kill -9
  • Change the port in your Dockerfile or Astro config if deploying locally

Issue 3: Environment Variables Not Loading

Error: Environment variables are undefined in your Astro app

Solutions:

  • Ensure variables are prefixed with PUBLIC_ if they need to be accessible in the browser
  • Check that environment variables are set in the Klutch.sh dashboard
  • For non-public variables, access them only in server-side code
  • Restart your app after adding environment variables

Issue 4: Static Assets Return 404

Error: CSS, images, or JavaScript not loading in production

Solutions:

  • Ensure all assets are in the public/ folder
  • Check that base path is correct in astro.config.mjs
  • Verify assets are referenced with correct paths (e.g., /image.png not image.png)
  • Check the browser console for 404 errors and adjust asset paths
  • Clear browser cache and redeploy

Issue 5: Slow Build Times

Error: Build takes longer than expected

Solutions:

  • Check for large dependencies or unnecessary imports
  • Use dynamic imports for heavy components
  • Optimize images before including them
  • Consider lazy-loading heavy content
  • Set BUILD_COMMAND: npm run build if using custom build steps

Best Practices

1. Use Layouts for Code Reuse

Leverage Astro layouts to reduce duplication:

src/layouts/BlogLayout.astro
---
import Layout from "./Layout.astro";
interface Props {
title: string;
author: string;
date: Date;
}
const { title, author, date } = Astro.props;
---
<Layout title={title}>
<article>
<h1>{title}</h1>
<p class="meta">
By {author} on {date.toLocaleDateString()}
</p>
<slot />
</article>
</Layout>

2. Implement Static Generation

Use Astro’s static generation for better performance:

src/pages/blog/[slug].astro
---
export async function getStaticPaths() {
const posts = await fetch("https://api.example.com/posts").then(r => r.json());
return posts.map(post => ({
params: { slug: post.slug },
props: { post },
}));
}
const { post } = Astro.props;
---
<h1>{post.title}</h1>
<p>{post.content}</p>

3. Optimize Images

Use Astro’s Image component for automatic optimization:

---
import { Image } from "astro:assets";
import myImage from "../images/my-image.png";
---
<Image
src={myImage}
alt="A descriptive image"
width={400}
height={300}
/>

4. Minimize JavaScript with Islands

Use Astro’s island architecture to include JavaScript only where needed:

src/pages/index.astro
---
import InteractiveCounter from "../components/Counter.jsx";
---
<h1>My Astro Site</h1>
<p>This is static HTML with zero JavaScript.</p>
<!-- Only this component ships JavaScript -->
<InteractiveCounter client:load />

5. Use CSS Modules

Scope styles to components using CSS modules:

src/components/Header.astro
---
import styles from "./Header.module.css";
---
<header class={styles.header}>
<h1 class={styles.title}>Welcome</h1>
</header>

6. Implement SEO Best Practices

Optimize your Astro site for search engines:

---
import { SEO } from "astro-seo";
interface Props {
title: string;
description: string;
canonicalURL?: string;
}
const { title, description, canonicalURL } = Astro.props;
---
<SEO
title={title}
description={description}
canonical={canonicalURL || Astro.url.pathname}
openGraph={{
basic: {
title,
type: "website",
image: "/og-image.png",
},
}}
/>

7. Cache Static Content

Configure caching headers for optimal performance:

src/middleware.ts
import { defineMiddleware } from "astro:middleware";
export const onRequest = defineMiddleware((context, next) => {
const response = next();
// Cache static assets for 1 year
if (
context.request.url.includes("/dist/") ||
/\.(js|css|woff2)$/.test(context.request.url)
) {
response.headers.set("Cache-Control", "public, max-age=31536000, immutable");
}
// Cache HTML pages for 1 hour
if (context.request.url.endsWith(".html") || !context.request.url.includes(".")) {
response.headers.set("Cache-Control", "public, max-age=3600, must-revalidate");
}
return response;
});

8. Monitor Performance Metrics

Track Core Web Vitals:

src/components/PerformanceMonitor.astro
---
<script>
// Monitor Web Vitals
import { getCLS, getFID, getFCP, getLCP, getTTFB } from "web-vitals";
getCLS(console.log);
getFID(console.log);
getFCP(console.log);
getLCP(console.log);
getTTFB(console.log);
</script>

9. Keep Dependencies Updated

Regularly update Astro and dependencies:

Terminal window
npm update @astrojs/* astro
npm audit fix

10. Test Before Deployment

Run tests and build locally before deploying:

Terminal window
npm run build
npm run preview
npm run lint
npm run type-check

Verifying Your Deployment

After deployment completes:

  1. Check the App URL: Visit your app at https://example-app.klutch.sh or your custom domain.
  2. Verify Pages Render: Ensure all pages load with proper styling and layout.
  3. Check Performance: Use Google PageSpeed Insights to verify performance.
  4. Test API Integration: If your app calls APIs, verify the requests work correctly.
  5. Review Console: Open the browser console (F12) and verify no errors are present.
  6. Check Headers: Use browser DevTools to verify security headers are present.
  7. Review Logs: Check the Klutch.sh dashboard logs for any runtime errors.

If your app doesn’t work as expected, review the troubleshooting section and check the logs in the Klutch.sh dashboard.


External Resources


Deploying an Astro application to Klutch.sh is straightforward with Nixpacks for automatic deployment or Docker for fine-grained control. By following this guide, you’ve learned how to scaffold an Astro project, create pages and components, configure environment variables, implement security best practices, set up monitoring, and troubleshoot common issues. Your Astro application is now running on Klutch.sh’s global infrastructure with automatic HTTPS, custom domain support, and production-grade performance. For additional help or questions, consult the official Astro documentation or contact Klutch.sh support.