/ astro-integrations / How to Use Deno Deploy with Astro: Complete Guide
astro-integrations 9 min read

How to Use Deno Deploy with Astro: Complete Guide

Step-by-step guide to integrating Deno Deploy with your Astro website.

How to Use Deno Deploy with Astro: Complete Guide

Deno Deploy is a serverless platform for deploying JavaScript and TypeScript at the edge. It runs your code on Deno's global network of edge servers, meaning your Astro site responds from the nearest location to each visitor. The result is extremely low latency. Astro has a Deno adapter for on-demand (server-side) rendering, which makes this one of the smoother deployment integrations available.

What makes Deno Deploy different from traditional Node.js hosting is that it uses the Deno runtime instead of Node. This means native TypeScript support, a security-first permissions model, and Web Standard APIs built in. For on-demand rendered Astro sites, this translates to fast cold starts and efficient edge rendering.

One important note before you start. The adapter changed hands. The old @astrojs/deno package is no longer the path forward (its latest release, 5.0.1, declares a peer dependency on Astro ^3.1.4 and predates Astro 6). The adapter is now maintained by the Deno team and published as @deno/astro-adapter. Everything below uses the current package.

Prerequisites

  • Node.js v22.12.0 or higher (Astro 6 dropped Node 18 and 20; odd-numbered versions like v23 are not supported)
  • Deno installed locally (optional, for testing the built server: curl -fsSL https://deno.land/install.sh | sh)
  • An Astro project (npm create astro@latest), running Astro 6 (current release is 6.4.2)
  • A Deno Deploy account (free tier available)
  • Your project in a GitHub repository (optional, for Git-connected deploys)

Installation

Install the current Astro Deno adapter. @deno/astro-adapter declares a peer dependency on astro: ^6.0.0, so make sure your project is on Astro 6.

npm install @deno/astro-adapter

If you work in a Deno-first toolchain, the adapter README shows the equivalent Deno command:

deno add npm:@deno/astro-adapter

Note: Even though you can develop with npm/Node locally, the Deno adapter builds an entry point that runs on the Deno runtime in production.

The adapter only matters for on-demand (server-side) rendering. A fully static Astro site does not need any adapter to deploy on Deno Deploy. See the static path in the deployment section below.

Configuration

Astro renders every route to static HTML by default. As the Astro docs put it, "By default, your entire Astro site will be prerendered, and static HTML pages will be sent to the browser." To render routes on demand you add an adapter and set output: 'server'.

Update your Astro config to use the Deno adapter:

// astro.config.mjs
import { defineConfig } from "astro/config";
import deno from "@deno/astro-adapter";

export default defineConfig({
  output: "server",
  adapter: deno(),
});

Important change from older guides: output: 'hybrid' no longer exists. It was removed in Astro 5, and static is the default. There is no third mode to pick. The correct pattern in Astro 6 is to set output: 'server' for an adapter-backed build, then keep most pages static with a per-route opt-in and render only the routes that need it on demand. The on-demand rendering docs state: when using output: 'server', "you can make individual pages static by adding export const prerender = true," and to render a single page on demand you add export const prerender = false at the top of that file.

The deno() adapter accepts a few options (per the adapter README): port (default 8085), hostname (default "0.0.0.0"), and start (a boolean controlling whether the generated entry script starts its own web server). If you set start: false, you import the exported handle function from the generated entry and wire it into your own Deno server.

Now connect your project to Deno Deploy. For a Git-connected project:

  1. Go to dash.deno.com and create a new project
  2. Connect your GitHub repository
  3. Set the entry point to dist/server/entry.mjs
  4. Set the build command to npm run build

Alternatively, use the deployctl CLI. Install it from JSR (the previous deno.land/x install line is outdated):

# Install deployctl from JSR
deno install -gArf jsr:@deno/deployctl

# Deploy the built server entry
deployctl deploy ./dist/server/entry.mjs

Environment variables are set in the Deno Deploy dashboard under your project's settings:

# In Deno Deploy Dashboard > Settings > Environment Variables
SITE_URL=https://yourdomain.com
API_KEY=your-api-key

Basic Usage

With the Deno adapter configured, your Astro site works the same way during development. The differences only show up in production. Write your pages and components normally:

---
// src/pages/index.astro
import BaseLayout from "../layouts/BaseLayout.astro";
---

<BaseLayout title="Home">
  <h1>Hello from the edge</h1>
  <p>This page is served from the nearest Deno Deploy edge location.</p>
</BaseLayout>

Server-rendered API routes work as expected:

// src/pages/api/time.ts
import type { APIRoute } from "astro";

export const GET: APIRoute = async ({ request }) => {
  const url = new URL(request.url);

  return new Response(
    JSON.stringify({
      time: new Date().toISOString(),
      region: Deno.env.get("DENO_REGION") || "unknown",
    }),
    {
      status: 200,
      headers: {
        "Content-Type": "application/json",
        "Cache-Control": "no-cache",
      },
    }
  );
};

Dynamic pages with server-side data fetching. Opt the route out of prerendering with export const prerender = false:

---
// src/pages/posts/[slug].astro
export const prerender = false; // Server-rendered at request time

const { slug } = Astro.params;
const response = await fetch(`https://api.example.com/posts/${slug}`);
const post = await response.json();

if (!post) {
  return Astro.redirect("/404");
}
---

<html>
  <head><title>{post.title}</title></head>
  <body>
    <h1>{post.title}</h1>
    <div set:html={post.content} />
  </body>
</html>

The build and deploy process:

# Build locally
npm run build

# Deploy to Deno Deploy (happens automatically on git push if connected)
git add .
git commit -m "Deploy to edge"
git push origin main

If you prefer to run the built server locally before shipping, the adapter README shows the exact Deno command (the entry needs network, read, and env permissions):

deno run --allow-net --allow-read --allow-env ./dist/server/entry.mjs

Production Tips

  1. Keep most pages static, opt routes into on-demand rendering. Not every page needs server rendering. Leave static as the default behavior, set output: 'server', and add export const prerender = false only on the routes that genuinely need request-time rendering. This reduces edge compute and improves response times for static content.

  2. Take advantage of edge caching. Add appropriate Cache-Control headers to your responses. Even on-demand pages can benefit from short cache durations (30-60 seconds) to reduce cold starts during traffic spikes.

  3. Use Deno's built-in KV store. Deno's platform includes Deno KV, a globally distributed key-value database. Use it for session storage, rate limiting, or caching API responses without adding an external database.

  • Monitor cold start times. Edge functions have cold starts. Keep your dependencies minimal and avoid importing large libraries in on-demand routes. The lighter your server bundle, the faster cold starts will be.

  • Set up preview deployments. Deno Deploy creates preview URLs for pull requests automatically when connected to GitHub. Use these for testing before merging to production.

  • Common Errors and Fixes

    • Cannot find package '@astrojs/deno' or a peer-dependency conflict on install. You are using the old, superseded package. Astro's own integration page notes the Deno adapter "was previously maintained by Astro but now is maintained by Deno directly," and advises existing users to migrate. Uninstall @astrojs/deno, install @deno/astro-adapter, and update the import in astro.config.mjs to import deno from "@deno/astro-adapter";.

    • output: 'hybrid' is an invalid option. hybrid was removed in Astro 5; there is no longer a hybrid mode. Use the default static for fully prerendered sites, or output: 'server' with an adapter, then control rendering per route with export const prerender = true | false. See the tracking issue withastro/astro#12278.

    • An adapter is required for on-demand rendering. If you set output: 'server' without an adapter, the build fails. Install @deno/astro-adapter and add adapter: deno() to your config.

    • deno: command not found when running deployctl deploy. The CLI is installed through Deno. Install it from JSR with deno install -gArf jsr:@deno/deployctl (the older deno.land/x/deploy/deployctl.ts install line is outdated). Make sure Deno itself is installed and on your PATH first.

    • PermissionDenied when running the built entry locally. The Deno runtime denies access by default. The adapter's documented run command grants the needed scopes: deno run --allow-net --allow-read --allow-env ./dist/server/entry.mjs.

    • Static deploys do not need the adapter. If your site is fully prerendered, do not add output: 'server' or the adapter. Astro's deploy guide shows deploying the static dist directory directly with Deno's file server (cd dist && deployctl deploy jsr:@std/http/file-server).

    • Deno is not defined in client-side code. Deno.env.get() and other Deno globals only exist on the server. Keep them inside frontmatter or API routes, never in a client:* component or a <script> that ships to the browser.

    Alternatives to Consider

    • Cloudflare Pages if you want edge deployment with a larger ecosystem of Workers, KV, and D1 database integrations.
    • Vercel if you prefer a more established platform with built-in image optimization and middleware support for Astro.
    • Netlify if you want edge functions with a broader feature set including forms, identity, and background functions.

    Wrapping Up

    Deno Deploy gives Astro sites true edge computing with minimal configuration thanks to the Deno-maintained adapter. Your on-demand pages render at the edge location closest to each visitor, which is hard to beat for global audiences. The free tier is generous enough for personal projects and small businesses, and the deployment workflow through GitHub is seamless. If you are building an on-demand Astro site and want the fastest possible response times worldwide, Deno Deploy is one of the most compelling options available. Just make sure you are on Astro 6 with @deno/astro-adapter, not the retired @astrojs/deno package.

    Official Docs and Examples

    Sources

    Checked on 2026-05-29: