> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/oven-sh/bun/llms.txt
> Use this file to discover all available pages before exploring further.

# Deploy with Docker

> Containerize your Bun application using the official oven/bun Docker images with a production-ready multi-stage Dockerfile.

[Docker](https://www.docker.com) is a platform for packaging and running an application as a lightweight, portable container that encapsulates all the necessary dependencies.

<Note>
  This guide assumes you already have [Docker Desktop](https://www.docker.com/products/docker-desktop/) installed.
</Note>

## Official images

Bun publishes official images to Docker Hub under [`oven/bun`](https://hub.docker.com/r/oven/bun/tags). Several variants are available:

| Tag               | Description                                          |
| ----------------- | ---------------------------------------------------- |
| `oven/bun:latest` | Latest stable release, based on Alpine Linux         |
| `oven/bun:1`      | Latest Bun 1.x release                               |
| `oven/bun:slim`   | Minimal Alpine-based image with no extras            |
| `oven/bun:debian` | Debian-based image for broader package compatibility |
| `oven/bun:canary` | Latest canary (pre-release) build                    |

## Multi-stage Dockerfile

A multi-stage build separates dependency installation from the final release image, keeping the production image small and fast.

```dockerfile Dockerfile theme={null}
# use the official Bun image
# see all versions at https://hub.docker.com/r/oven/bun/tags
FROM oven/bun:1 AS base
WORKDIR /usr/src/app

# install dependencies into temp directory
# this will cache them and speed up future builds
FROM base AS install
RUN mkdir -p /temp/dev
COPY package.json bun.lock /temp/dev/
RUN cd /temp/dev && bun install --frozen-lockfile

# install with --production (exclude devDependencies)
RUN mkdir -p /temp/prod
COPY package.json bun.lock /temp/prod/
RUN cd /temp/prod && bun install --frozen-lockfile --production

# copy node_modules from temp directory
# then copy all (non-ignored) project files into the image
FROM base AS prerelease
COPY --from=install /temp/dev/node_modules node_modules
COPY . .

# [optional] tests & build
ENV NODE_ENV=production
RUN bun test
RUN bun run build

# copy production dependencies and source code into final image
FROM base AS release
COPY --from=install /temp/prod/node_modules node_modules
COPY --from=prerelease /usr/src/app/index.ts .
COPY --from=prerelease /usr/src/app/package.json .

# run the app
USER bun
EXPOSE 3000/tcp
ENTRYPOINT [ "bun", "run", "index.ts" ]
```

## .dockerignore

Create a `.dockerignore` file to exclude files from the build context. This speeds up builds and prevents secrets from leaking into the image.

```text .dockerignore theme={null}
node_modules
Dockerfile*
docker-compose*
.dockerignore
.git
.gitignore
README.md
LICENSE
.vscode
.env
coverage*
```

## Build and run

<Steps>
  <Step title="Build the image">
    The `-t` flag names the image and `--pull` ensures Docker downloads the latest base image.

    ```bash theme={null}
    docker build --pull -t bun-app .
    ```
  </Step>

  <Step title="Run the container">
    Run in detached mode (`-d`) and map port 3000 on the container to port 3000 on your machine.

    ```bash theme={null}
    docker run -d -p 3000:3000 bun-app
    ```

    Visit [http://localhost:3000](http://localhost:3000) to confirm the app is running.
  </Step>

  <Step title="Stop the container">
    List running containers and stop by ID.

    ```bash theme={null}
    docker ps
    docker stop <container-id>
    ```
  </Step>
</Steps>

## Docker Compose

For local development with multiple services, use Docker Compose:

```yaml docker-compose.yml theme={null}
services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 10s
    restart: unless-stopped

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:
```

Start the stack with:

```bash theme={null}
docker compose up -d
```

## Health checks

Add a health check endpoint to your Bun server so Docker can monitor the container:

```typescript index.ts theme={null}
Bun.serve({
  port: 3000,
  fetch(req) {
    const url = new URL(req.url);

    if (url.pathname === "/health") {
      return new Response("OK", { status: 200 });
    }

    return new Response("Hello from Bun!");
  },
});
```

The `HEALTHCHECK` instruction in the Dockerfile (or the `healthcheck` key in Compose) will poll this endpoint and mark the container as unhealthy if it fails.

<Tip>
  Use `oven/bun:debian` instead of the default Alpine image when you need to install native packages (e.g., `libvips` for image processing) that require a glibc-based system.
</Tip>
