I find myself coming back to Bun more and more lately for running scripts and side projects. It’s fast, has great DX, and is a joy to work with. For a recent project I wanted to deploy a Bun app inside a Docker container. Here’s what I learned.
Running Bun in Alpine Linux
When building production Docker images, you often want to use a minimal base image to keep the image size small. Alpine Linux is a popular choice for this because it’s lightweight and secure.
Like many Docker repositories, oven/bun also provides an image based on Alpine. So you don’t have to create your own docker file from scratch and can use the official bun docker image as a base image for your own Dockerfile.
🔥 Hot Tip: Use a fixed version tag like 1.1.10-alpine
instead of latest
to ensure reproducibility and avoid unexpected changes in your production environment. Then use tools like Dependabot to keep your images up to date.
The Dockerfile
You will probably want to have a build stage in your Dockerfile to create a production build of your Bun application. While bundle size is not as critical on the server as it is in the browser, you still want to get performance optimizations, environment-specific behavior (like logging or error handling), and also take advantage of tree-shaking and minification.
Have you ever pushed a bloated Docker image over Wi-Fi to Docker Hub? It’s not fun. 🐌
Multi-stage Dockerfile
In order to keep you image size small, you can leverage multi-stage builds in Docker. This allows you to use multiple FROM
statements in your Dockerfile. Each FROM
instruction can use a different base, and you can copy artifacts from one stage to another.
In the end you will only have the production build of your application as well as the runtime dependencies in your final image. This not only keeps the image size small but also makes it more secure by limiting the attack surface.
🔥 Hot Tip: If your build step needs packages that aren’t available in Alpine, you can use another distro like Ubuntu to build it and then deploy it with Alpine.
Bun and Shebangs
As the docs say, bun
and bunx
both respect shebangs. This means that scripts that use #!/usr/bin/env node
, for example, will run in Node and not in Bun.
This is something you might not notice when you’re working in development, since you probably have Node installed on your local machine. But the official Bun Docker image doesn’t come with Node installed.
You might be wondering why this is a problem. While Bun claims to be Node-compatible, there are still some edge cases and differences in behavior. For example, I noticed issues when working with earlier versions of Drizzle Kit and some events not firing when using AbortController
in Astro running in Bun.
🔥 Hot Tip: Use the --bun
CLI flag to make scripts always run in Bun: bun --bun run index.ts
Conclusion
By leveraging multi-stage builds and a minimal base image like Alpine Linux, you can keep your Docker images small and efficient for production environments. Be mindful of Bun’s handling of shebangs to avoid compatibility issues, and consider using the --bun
flag to ensure scripts run correctly.