Deno reimagines the JavaScript runtime with security baked in: no file, network, or environment access unless you explicitly grant it. That permission model is great for safety but means deployment requires a little more intentionality. This guide covers a production Deno deployment.
The permission model is a deployment concern
Unlike Node, Deno runs with zero permissions by default. You opt in per capability:
deno run --allow-net --allow-env --allow-read main.tsIn production, grant the minimum your app needs. A typical web service needs:
--allow-net— to listen and make outbound requests.--allow-env— to read configuration andDATABASE_URL.--allow-read— if it reads files (static assets, templates).
You can scope flags further, e.g. --allow-net=0.0.0.0:8000,db.example.com to whitelist specific hosts. This is real, enforceable least-privilege — use it.
A production Dockerfile
Deno publishes official images. Cache dependencies in a layer for faster rebuilds:
FROM denoland/deno:2.0.0
WORKDIR /app
# Cache deps separately for layer caching
COPY deno.json deno.lock ./
RUN deno install
COPY . .
RUN deno cache main.ts
EXPOSE 8000
USER deno
CMD ["run", "--allow-net", "--allow-env", "main.ts"]deno cache main.ts pre-downloads and compiles dependencies at build time, so the container starts fast and works offline. The official image already includes a non-root deno user.
Writing a server
Deno's built-in HTTP server uses web-standard APIs:
const port = Number(Deno.env.get("PORT") ?? 8000);
Deno.serve({ port, hostname: "0.0.0.0" }, (req) => {
const url = new URL(req.url);
if (url.pathname === "/healthz") {
return new Response(JSON.stringify({ status: "ok" }), {
headers: { "content-type": "application/json" },
});
}
return new Response("Hello from Deno");
});Bind to 0.0.0.0 so the container is reachable externally. Deno.serve is production-grade and handles concurrency natively.
A managed database
Deno has first-class PostgreSQL drivers. Using deno-postgres:
import { Pool } from "https://deno.land/x/postgres/mod.ts";
const pool = new Pool(Deno.env.get("DATABASE_URL")!, 5, true);
const client = await pool.connect();
try {
const result = await client.queryObject`SELECT NOW()`;
console.log(result.rows);
} finally {
client.release();
}The pool's third argument (true) makes connections lazy. Keep the pool size aligned with your database's connection limit across replicas.
Compiling to a single binary (optional)
Deno can compile your app into a self-contained executable:
deno compile --allow-net --allow-env --output server main.tsThe permissions are baked into the binary, and you get a single artifact with no runtime to install — handy for a distroless-style image. For most cloud deployments the container approach above is simpler.
Deploying on PandaStack
- 1Create a PostgreSQL database —
DATABASE_URLis injected automatically. - 2Connect your repo as a container app. PandaStack detects the Dockerfile and builds via rootless BuildKit.
- 3Set any secrets and
PORThandling in the dashboard. - 4Push — live build logs stream and you get automatic SSL on custom domains.
Deno's fast startup makes it a good fit for free-tier scale-to-zero. You also get rollbacks and deploy history.
| Concern | Setting |
|---|---|
| Permissions | --allow-net --allow-env (scope further) |
| Server | Deno.serve on 0.0.0.0 |
| Deps | deno cache at build time |
| User | non-root deno |
| DB | deno-postgres pool |
Common pitfalls
- Over-granting permissions —
--allow-alldefeats Deno's main advantage; scope flags. - Binding to
localhost— unreachable inside a container; use0.0.0.0. - No lockfile — commit
deno.lockfor reproducible, verifiable dependency builds. - Skipping
deno cache— slower cold starts and network dependency at runtime.
References
- Deno deployment guide: https://docs.deno.com/runtime/tutorials/docker/
- Deno permissions reference: https://docs.deno.com/runtime/fundamentals/security/
- Deno.serve API: https://docs.deno.com/api/deno/~/Deno.serve
- deno-postgres driver: https://deno.land/x/postgres
- deno compile: https://docs.deno.com/runtime/reference/cli/compile/
---
Deno's quick startup and PandaStack's free-tier scale-to-zero are a natural match. Add a managed PostgreSQL database with DATABASE_URL auto-wired and push your repo. Deploy at https://dashboard.pandastack.io