Introduction
Setting up a monorepo architecture with proper tooling can significantly improve your development workflow. Bun, the all-in-one JavaScript runtime and toolkit, offers excellent workspace support that simplifies managing multiple interconnected packages. This guide walks you through creating a Bun workspace and containerizing it with Docker — perfect for modern full-stack applications.
What You'll Learn
- How to structure a Bun workspace with shared packages
- Configuring TypeScript for cross-package imports
- Containerizing your application with Docker
- Setting up a complete development environment
Project Structure
Let's start with the folder structure we'll be creating:
├── apps/
│ ├── client/ # Frontend application
│ └── server/ # Backend application
├── infra/
│ ├── docker/ # Docker images
│ │ ├── client.Dockerfile
│ │ └── server.Dockerfile
├── packages/
│ └── shared/ # Shared code between apps
├── docker-compose.yml
└── package.json
This structure follows the monorepo pattern where:
-
apps
contains your applications (client and server) -
packages
contains shared libraries used across applications -
infra
holds infrastructure-related files like Dockerfiles
Step 1: Initialize the Main Project
First, let's initialize our root project:
bun init -y
Then modify your package.json
to configure workspaces:
{
"name": "workspace",
"private": true,
"workspaces": [
"packages/*",
"apps/*"
],
"type": "module",
"devDependencies": {
"@types/bun": "latest"
}
}
The workspaces
field tells Bun to treat subdirectories in packages
and apps
as separate packages within our monorepo.
Step 2: Set Up the Shared Package
Create a shared package that will contain code used by both frontend and backend:
mkdir -p packages/shared
cd packages/shared
bun init -y
Update the packages/shared/package.json
:
{
"name": "@packages/shared",
"version": "1.0.0",
"type": "module",
"main": "./dist/index.js",
"scripts": {
"build": "bun build ./src/index.ts --outdir ./dist --target node"
}
}
Create a basic source file:
mkdir -p packages/shared/src
echo 'export const greet = (name: string) => `Hello, ${name}!`;' > packages/shared/src/index.ts
Step 3: Set Up the Applications
Backend Application
Set up the server application:
mkdir -p apps/server/src
cd apps/server
bun init -y
Update apps/server/package.json
to include the shared package:
{
"name": "@apps/server",
"version": "1.0.0",
"type": "module",
"dependencies": {
"@packages/shared": "workspace:*"
},
"scripts": {
"dev": "bun run --watch src/index.ts",
"start": "bun run src/index.ts"
}
}
Create a tsconfig.json
in the server directory:
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "node",
"esModuleInterop": true,
"strict": true,
"paths": {
"@packages/shared": [
"../../packages/shared/src"
]
}
}
}
Create a simple server:
// apps/server/src/index.ts
import { greet } from '@packages/shared';
import { serve } from 'bun';
const server = serve({
port: 3000,
fetch(req) {
const url = new URL(req.url);
if (url.pathname === '/health') {
return new Response('OK');
}
if (url.pathname === '/') {
return new Response(greet('from the server'));
}
return new Response('Not Found', { status: 404 });
},
});
console.log(`Server running at http://localhost:${server.port}`);
Frontend Application
Set up the client application similarly:
mkdir -p apps/client/src
cd apps/client
bun init -y
Update apps/client/package.json
:
{
"name": "@apps/client",
"version": "1.0.0",
"type": "module",
"dependencies": {
"@packages/shared": "workspace:*"
},
"scripts": {
"dev": "bun run --watch src/index.ts",
"start": "bun run src/index.ts"
}
}
Create a tsconfig.json
in the client directory:
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "node",
"esModuleInterop": true,
"strict": true,
"paths": {
"@packages/shared": [
"../../packages/shared/src"
]
}
}
}
Step 4: Dockerize Your Applications
Server Dockerfile
Create infra/docker/server.Dockerfile
:
FROM oven/bun:1-slim
WORKDIR /app
# Copy only package files first
COPY package.json bun.lockb ./
COPY packages/shared/package.json ./packages/shared/
COPY apps/server/package.json ./apps/server/
# Install dependencies
RUN bun install --filter '@apps/server'
# Copy source files
COPY apps/server ./apps/server
COPY packages/shared ./packages/shared
# Build shared package
RUN cd packages/shared && bun run build
EXPOSE 3000
ENTRYPOINT ["bun", "run", "apps/server/src/index.ts"]
Step 5: Create Docker Compose File
Create docker-compose.yml
in the root directory:
version: '3.8'
services:
...
server:
build:
context: .
dockerfile: infra/docker/server.Dockerfile
ports:
- "3000:3000"
env_file:
- apps/server/.env
restart: unless-stopped
healthcheck:
test:
[
"CMD",
"wget",
"--no-verbose",
"--tries=1",
"--spider",
"http://localhost:3000/health",
]
interval: 30s
timeout: 10s
retries: 3
Step 6: Install Dependencies and Run
From the root directory, install all dependencies:
bun install
You can now start everything with Docker Compose:
docker-compose up -d
Or run individual components in development mode:
# Run the server
cd apps/server
bun run dev
# Run the client
cd apps/client
bun run dev
Benefits of This Setup
Code Sharing: The shared package can contain utilities, types, constants, and business logic used by both frontend and backend.
Type Safety: TypeScript configuration ensures type safety across packages.
Development Experience: Changes in shared packages are immediately available to dependent applications.
Production Ready: Docker images are optimized for production deployment.
Scalability: This structure makes it easy to add more applications or packages as your project grows.
Conclusion
You now have a fully functional Bun workspace setup with Docker integration. This architecture promotes code sharing, provides excellent developer experience, and simplifies deployment.
For a complete working example with React frontend and a Bun backend with Prisma, visit the bun-workspaces-docker GitHub repository.
Happy coding with Bun!
Top comments (1)
Hey there! I'm super excited you found this guide helpful! If you run into any snags while setting up your Bun workspace, drop a comment below and I'll help you out. Don't forget to check out the GitHub repo for more examples. Happy coding! 👋