How to Dockerize a Sveltekit app with Prisma and Bun
Learn how to containerize a SvelteKit app with Prisma and Bun using a multi-stage Dockerfile. Discover workarounds for Prisma compatibility, the benefits of distroless images, and the importance of running non-root containers for enhanced security.
If you're trying to use Prisma with Bun's official Docker image, you may have encountered issues. Unfortunately, Prisma doesn't fully support Bun's official image yet (though I'm not sure it isn't a runtime issue?). There’s an open issue tracking this limitation. Thankfully, there’s a workaround: using the imbios/bun-node
image, which includes Node.js alongside Bun. Prisma can fall back to Node.js for compatibility, allowing you to build your application before transitioning to the oven/bun runner image.
Below is a multi-stage Dockerfile for containerizing your SvelteKit app with Prisma and Bun:
FROM imbios/bun-node:latest-iron-slim AS base
WORKDIR /app
# Copy essential files
COPY package*.json bun.lockb ./
COPY prisma ./prisma
COPY docker ./docker
# Dependencies stage: installs application dependencies
FROM base AS dependencies
RUN bun install --frozen-lockfile
# Builder stage: compiles the application
FROM dependencies AS builder
COPY . .
RUN bun run build
# Ensure the build directory exists to avoid runtime errors
RUN test -d build || (echo "Build directory not found" && exit 1)
# Preparation stage: sets up user and directory permissions
FROM builder AS prep
# Add a non-root user for enhanced security
RUN if ! getent passwd 1001 >/dev/null; then \
echo "UID 1001 is available. Adding 'sveltekit' user with UID 1001." && \
adduser --disabled-login --gecos "" --home /app --uid 1001 --ingroup bun sveltekit; \
else \
echo "UID 1001 is already in use. Not creating 'sveltekit' user."; \
fi
# Create a writable data directory for application state
RUN mkdir -p /app/data && chown -R 1001:1001 /app/data
# Final stage: runs the application using Bun's official lightweight image
FROM oven/bun:1-slim AS runner
WORKDIR /app
# Set environment variables for production
ENV NODE_ENV=production
ENV PUBLIC_NODE_ENV=production
# Copy application files from the prep stage
COPY --chown=1001:1001 --from=prep /app/build ./build
COPY --chown=1001:1001 --from=prep /app/package.json ./package.json
COPY --chown=1001:1001 --from=prep /app/node_modules ./node_modules
COPY --chown=1001:1001 --from=prep /app/static ./static
COPY --chown=1001:1001 --from=prep /app/prisma ./prisma
COPY --chown=1001:1001 --from=prep /app/docker ./docker
COPY --chown=1001:1001 --from=prep /app/data ./data
# Switch to the non-root user for running the app
USER 1001
# Declare the data directory as a Docker volume
VOLUME ["/app/data"]
# Expose the default port for the SvelteKit application
EXPOSE 5173/tcp
# Set the entry point for the container
ENTRYPOINT ["bun", "run", "start"]
You may consider using a Bun Distroless Docker image to enhance security. Note that Distroless images and non-root containers address different security aspects.
What's a Distroless Image
A distroless image removes unnecessary components like package managers, shells, and utilities typically found in standard Linux distributions. It contains only the application and its runtime dependencies. This reduces the attack surface.
What are Non-Root Containers
A non-root container runs the application using a user with limited privileges instead of the docker's default root
user. This mainly prevents privilege escalation attacks. If an attacker compromises the container, they won’t have root access to the host system.