Back to Blog
Tutorial8 min read2026-07-23

Deploy a Deno Oak API with Postgres on PandaStack

A complete, step-by-step tutorial for deploying a production-ready Deno Oak API with a managed PostgreSQL database to PandaStack using a Git-push workflow.

Ajay Kumar
Ajay Kumar
Founder & DevOps, PandaStack

Deno has matured into a fantastic runtime for building secure, modern web services. Its built-in tooling, first-class TypeScript support, and security-by-default model are compelling. Oak is one of the most popular and robust middleware frameworks for Deno, providing an Express-like experience for building APIs.

But how do you take your Deno Oak app from localhost to a production environment with a managed database, CI/CD, and custom domains?

My name is Ajay Kumar, and as the Founder and DevOps lead at PandaStack, I spend my days building the infrastructure that makes this process seamless. In this post, I'll give you a complete, no-fluff guide to deploying a Deno Oak API with a PostgreSQL database on PandaStack. We'll cover everything from structuring your app to the specific configuration needed for a smooth Git-push deployment.

Prerequisites

Before we start, make sure you have the following:

* [Deno](https://deno.land/#installation) installed on your local machine.

* [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) installed and configured.

* A GitHub, GitLab, or Bitbucket account.

* A free [PandaStack account](https://pandastack.io).

Step 1: Create a Basic Oak API

Let's start with a simple, well-structured Oak server. We'll manage our dependencies in a deps.ts file and write our server logic in main.ts.

Create a new project directory:

mkdir deno-oak-api
cd deno-oak-api

First, create deps.ts to manage our third-party modules. This makes versioning explicit and keeps our main file clean.

// deps.ts
export { Application, Router } from "https://deno.land/x/oak@v12.6.1/mod.ts";
export { Pool } from "https://deno.land/x/postgres@v0.17.0/mod.ts";

Next, create main.ts. The most critical part for any PaaS deployment is listening on the port provided by the environment. PandaStack (and others) inject a PORT environment variable that your application *must* bind to.

// main.ts
import { Application, Router } from "./deps.ts";

const app = new Application();
const router = new Router();

// A simple health check route
router.get("/health", (ctx) => {
  ctx.response.body = { status: "ok", timestamp: new Date() };
  ctx.response.status = 200;
});

// A basic root route
router.get("/", (ctx) => {
  ctx.response.body = "Hello from Deno and Oak on PandaStack!";
});

app.use(router.routes());
app.use(router.allowedMethods());

// Critical: Use the PORT environment variable
const port = Deno.env.get("PORT") || 8000;

console.log(`Server listening on http://localhost:${port}`);
await app.listen({ port: parseInt(port) });

You can run this locally to test it:

deno run --allow-net --allow-env main.ts

You should see Server listening on http://localhost:8000 and be able to access http://localhost:8000/health.

Step 2: Connect to a PostgreSQL Database

A static API is nice, but most real-world applications need a database. PandaStack can provision a managed PostgreSQL database and automatically inject the connection string as a DATABASE_URL environment variable.

Let's modify main.ts to connect to Postgres using the postgres.js library we imported in deps.ts.

// main.ts (updated)
import { Application, Router, Pool } from "./deps.ts";

// Get the database URL from the environment
const DATABASE_URL = Deno.env.get("DATABASE_URL");

// Create a connection pool. 
// Important: Handle the case where DATABASE_URL is not set (e.g., local dev without a DB)
const pool = DATABASE_URL
  ? new Pool(DATABASE_URL, 3, true) // 3 connections, lazy loading
  : null;

const app = new Application();
const router = new Router();

// Health check route
router.get("/health", (ctx) => {
  ctx.response.body = { status: "ok", timestamp: new Date() };
});

// Root route
router.get("/", (ctx) => {
  ctx.response.body = "Hello from Deno and Oak on PandaStack!";
});

// New route to test database connection
router.get("/db", async (ctx) => {
  if (!pool) {
    ctx.response.status = 503; // Service Unavailable
    ctx.response.body = { error: "Database not configured" };
    return;
  }
  
  try {
    const connection = await pool.connect();
    const result = await connection.queryObject`SELECT NOW() as now;`;
    ctx.response.body = { db_time: result.rows[0].now };
    connection.release();
  } catch (err) {
    console.error(err);
    ctx.response.status = 500;
    ctx.response.body = { error: "Failed to connect to the database." };
  }
});

app.use(router.routes());
app.use(router.allowedMethods());

const port = Deno.env.get("PORT") || 8000;

console.log(`Server listening on http://localhost:${port}`);
await app.listen({ port: parseInt(port) });

Notice we've added a /db route that queries the current time from PostgreSQL. We also gracefully handle the case where DATABASE_URL isn't set, which is good practice for local development.

Step 3: Configure for Production Deployment

This is where we tell PandaStack how to build and run our Deno application. While PandaStack auto-detects many frameworks, Deno's unique runtime requires a little explicit configuration. The best way to do this is with a Procfile.

Create a file named Procfile (no extension) in your project root:

web: deno run --allow-net --allow-env main.ts

Why this command?

* web:: This declares a "web" process type, which PandaStack will expose to the internet and route traffic to.

* deno run: The standard command to execute a Deno script.

* --allow-net: Grants the process network access. This is essential for the web server to bind to a port and for our code to connect to the PostgreSQL database.

* --allow-env: Grants permission to read environment variables like PORT and DATABASE_URL. Deno's secure-by-default nature requires you to opt-in to these capabilities.

With main.ts, deps.ts, and Procfile in place, commit your code and push it to a Git repository.

git init
git add .
git commit -m "Initial Deno Oak API"
# Link to your remote GitHub/GitLab/Bitbucket repo and push
git remote add origin <your-repo-url>
git push -u origin main

Step 4: Deploying on PandaStack

Now for the easy part. Log into your PandaStack dashboard.

  1. 1 Create a New Service: Click "New Service" and select "Web Service".
  2. 2 Connect Your Repo: Connect to the Git provider where you pushed your code and select your deno-oak-api repository.
  3. 3 Configure the Service:

* Service Name: Give your app a name, like deno-api.

* Build Command (Optional but Recommended): While not strictly required for this simple app, it's good practice to cache dependencies during the build step. This speeds up subsequent container startups. Set the build command to:

deno cache deps.ts

* Start Command: PandaStack will automatically detect and use the web command from your Procfile. You can leave this field blank. If you didn't use a Procfile, you would enter deno run --allow-net --allow-env main.ts here.

* Instance Type: For a hobby project, the Free tier instance (0.25 CPU, 512MB RAM) is plenty. Paid plans offer more powerful compute options and run on stable nodes without scale-to-zero.

  1. 1 Add a Database:

* Before deploying, scroll down to the "Add-ons" section.

* Click "Add Database" and choose "New Managed Database".

* Select PostgreSQL (e.g., version 16), choose a plan (the Free tier is fine to start), and give it a name.

* PandaStack will automatically provision the database and link it to your web service. The DATABASE_URL environment variable will be injected into your application's environment automatically. No manual credential handling required.

  1. 1 Deploy:

* Click "Create Service".

PandaStack will now pull your code, run the build command (deno cache deps.ts), build a container image, provision your database, and deploy your application. You can watch the build and deploy logs in real-time right from the dashboard.

Step 5: Go Live and Verify

Once the deployment finishes, your service will be marked as "Live".

* Your URL: PandaStack provides an initial public URL like https://deno-api.pandastack.app.

* Verify Health: Open https://your-app-url/health in your browser. You should see the JSON response: { "status": "ok", ... }.

* Verify Database: Open https://your-app-url/db. You should see a response with the current time from your managed PostgreSQL database: { "db_time": "..." }.

A Note on the Free Tier: PandaStack's free tier is designed for hobby projects and development. Services on this tier will "scale to zero" after a period of inactivity to conserve resources. This means the first request to an idle app might experience a "cold start" delay of a few seconds. For production applications that need to be instantly available, our Pro ($15/mo) and Premium ($25/mo) plans run on dedicated, always-on instances.

Adding a Custom Domain:

In the "Domains" tab for your service, you can easily add your own custom domain. Just enter the domain name, and PandaStack will provide the necessary DNS records (usually a CNAME) and automatically provision and renew a free SSL certificate for you.

Conclusion

You've successfully deployed a Deno Oak API with a managed PostgreSQL database using a simple git push workflow.

We covered the key ingredients for a successful deployment on a modern PaaS:

* Reading the PORT and DATABASE_URL from the environment.

* Using a Procfile to define the start command.

* Explicitly granting permissions (--allow-net, --allow-env) required by the Deno runtime.

The beauty of this setup is its simplicity and reproducibility. Any changes you push to your main branch will automatically trigger a new deployment. PandaStack handles the entire pipeline of building, deploying, and managing the underlying infrastructure, letting you focus on your code.

Ready to try it yourself? [Deploy your own Deno app on PandaStack](https://pandastack.io) and see how quickly you can go from code to live.

Ready to deploy?

Start free on PandaStack.

Start free on PandaStack

More in Tutorial

Browse all Tutorial articles →

See also