Bun and Deno on a Container Host: The Dockerfiles, the Permissions, and What Is Not Detected for You
Bun and Deno both run TypeScript without a build step, start fast and use less memory than Node for the same service, which makes them a natural fit for a 512 MB container. What they do not get on most container hosts is automatic detection: a repository with bun.lock or deno.json looks like a Node project to a generator that only knows package.json. On SnapDeploy the generated Dockerfile is Node-only, so for either runtime you bring a Dockerfile. Here is one for each, with the permissions, ports and lock-file details that decide whether the first deploy works.
Tested with Bun 1.4 and Deno 2.9, the current stable releases in September 2026. Both publish multi-architecture images, so they run unchanged on a host that builds ARM64 images.
Bun
// src/index.ts
const port = Number(Bun.env.PORT ?? 3000);
Bun.serve({
port,
hostname: "0.0.0.0",
fetch(req) {
const url = new URL(req.url);
if (url.pathname === "/health") return new Response("ok");
if (url.pathname === "/api/time") return Response.json({ now: new Date().toISOString() });
return new Response("not found", { status: 404 });
},
});
console.log(`listening on :${port}`);FROM oven/bun:1-alpine AS deps
WORKDIR /app
COPY package.json bun.lock ./
RUN bun install --frozen-lockfile --production
FROM oven/bun:1-alpine
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
ENV NODE_ENV=production
USER bun
EXPOSE 3000
CMD ["bun", "run", "src/index.ts"]oven/bun:1-alpinetracks the current 1.x release; pinoven/bun:1.4-alpinefor reproducible builds. The Alpine image is about 100 MB; the Debian-based default is larger.--frozen-lockfilefails the build ifbun.lockis out of date, which is what you want on a hosted build. Commit the lock file.- Bun runs TypeScript directly; there is no build step unless you want one. For a smaller image,
bun build --compileproduces a single executable you can copy onto a bare Alpine stage. - The
bunuser exists in the official image; run as it.
Memory: the service above idles around 30 to 40 MB, roughly half of the equivalent Node process. Frameworks on top (Hono, Elysia, or Express, which Bun runs through its Node compatibility) add a little. Start-up is well under a second.
Deno
// main.ts
const port = Number(Deno.env.get("PORT") ?? "8000");
Deno.serve({ port, hostname: "0.0.0.0" }, (req) => {
const { pathname } = new URL(req.url);
if (pathname === "/health") return new Response("ok");
if (pathname === "/api/time") return Response.json({ now: new Date().toISOString() });
return new Response("not found", { status: 404 });
});FROM denoland/deno:alpine
WORKDIR /app
COPY deno.json deno.lock ./
COPY . .
RUN deno cache main.ts
USER deno
EXPOSE 8000
CMD ["deno", "run", "--allow-net", "--allow-env", "main.ts"]deno cacheat build time downloads and compiles dependencies into the image so the container does not fetch anything at start. With adeno.lockcommitted, the build fails if the lock does not match, which again is what you want.- Permissions are explicit:
--allow-netto listen and make requests,--allow-envto readPORTand secrets. Add--allow-readonly if the app reads files. Granting-A(everything) works but throws away the one thing Deno does that Node does not. denoland/deno:alpineis the small image; pindenoland/deno:2.9.6(Debian-based) or the matching Alpine tag for reproducible builds.- For a single-binary image,
deno compileproduces an executable you can copy onto a bare stage, at the cost of a longer build.
Memory is similar to Bun, around 30 to 50 MB idle for a small service; npm packages imported through npm: specifiers work and are cached the same way.
What the platform does and does not do for these
- Does: use your Dockerfile as written, read the port from
EXPOSE, inject environment variables and add-on connection strings, build for ARM64 (both official images support it), show build and runtime logs. - Does not: detect Bun or Deno from the repository. A
package.jsonnext tobun.lockwill be treated as a Node project and started withnpm starton a Node image, which fails for anything that relies on Bun APIs. The Dockerfile settles it. - Does not: scan Bun or Deno code for environment variables. Add a
.env.exampleto the repository and the variables it lists are offered to you before the first deploy.
Deploying either
- Commit the Dockerfile, the lock file and a
.dockerignore(node_modules,.git,.env). - Push. Containers → Deploy from GitHub → pick the repository. Port 3000 for the Bun example, 8000 for Deno.
- Add environment variables; attach a database add-on if needed (
DATABASE_URLis injected). - Deploy, then open
/healthand/api/timeon the container URL.
Test the image before you push
docker build -t svc .
docker run --rm -p 3000:3000 -e PORT=3000 svc
curl -s http://localhost:3000/healthThe two failures you will meet are both visible here: a lock file that does not match (build fails at install) and a Deno permission you forgot (the process exits on the first denied call, with a line telling you which flag to add).
A database
Both runtimes talk to Postgres with the same libraries Node uses (postgres, pg, or Drizzle on top), and Deno has its own @db/postgres. Read DATABASE_URL from the environment, cap the pool at a few connections, and pick the database the same way every guide here does: a PostgreSQL Mini ($29/month), a $1 DB Sprint Pack for 12 hours, or a free external plan such as Neon.
Where free stops
- Sleep after 15 minutes without traffic, about a minute to wake; both runtimes start so fast that the wake is almost entirely the container itself.
- 100 container-hours a month across four containers; a small service visited occasionally uses a fraction.
- Custom domains and no sleeping: Always-On, $12/month per container.
Picking one
If the project already has package.json and npm dependencies, Bun is the drop-in: same layout, faster installs, and most Node libraries work. If you are starting fresh and want permissions, first-class TypeScript and a standard library without a package manager, Deno is the cleaner design. On a container both are the same size and speed for a small service; the choice is about the ecosystem you want, not the host.
Free tier: 4 containers, 512 MB and 0.25 vCPU each, 100 container-hours a month, 10 deploys a day, no card; details on the free tier docs page. Bun and Deno versions as published on bun.com and deno.com in September 2026.