You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Haskell Web应用Docker镜像构建最佳实践:多阶段与快速重建

Docker Best Practices for Haskell Web Apps (postgresql-simple + Stack)

Awesome question! Let's walk through the exact Docker setup you need for your Haskell web app that hits both your requirements: a tiny production-ready image via multi-stage builds, and smart caching to avoid recompiling dependencies every time your code changes.

1. Multi-Stage Builds: Keep Final Images Lean

Multi-stage builds split the process into two focused parts: a heavy "builder" image with all tools needed to compile your app, and a minimal "runtime" image that only includes what's required to run it. This eliminates bloat like GHC, Stack, or build dependencies from your final deployment image.

Builder Stage (Compile Everything)

We’ll use the official haskell Docker image as our base—it comes pre-installed with Stack and GHC. We also need to install libpq-dev (PostgreSQL development files) since postgresql-simple depends on libpq.

Runtime Stage (Only What's Needed to Run)

For the final image, we’ll use a slim Debian image (or Alpine for even smaller size) and only install the runtime version of libpq (libpq5 on Debian) plus your compiled executable.

2. Dependency Caching: Avoid Recompiling Dependencies

Docker caches each layer of your build, so we can optimize by copying dependency definition files before your application code. This way, Docker will only recompile dependencies if your stack.yaml, stack.yaml.lock, or package.yaml (or .cabal files) change—code updates won’t trigger a full dependency rebuild.

Full Dockerfile Example

Here’s a complete, tested Dockerfile that implements both practices:

# ------------------------------
# Builder Stage: Compile the app
# ------------------------------
FROM haskell:9.4-bullseye as builder

# Install system-level build dependencies for postgresql-simple
RUN apt-get update && \
    apt-get install -y --no-install-recommends libpq-dev && \
    rm -rf /var/lib/apt/lists/*

# Set working directory
WORKDIR /app

# Copy dependency files FIRST to leverage Docker cache
COPY stack.yaml stack.yaml.lock package.yaml ./

# If you use .cabal files instead of package.yaml, replace the line above with:
# COPY stack.yaml stack.yaml.lock your-app.cabal ./

# Build ONLY the dependencies (cached unless dependency files change)
RUN stack build --only-dependencies

# Now copy the rest of your source code
COPY app/ app/
COPY src/ src/
COPY test/ test/

# Build and install the executable to a dedicated bin directory
RUN stack install --local-bin-path /app/bin

# ------------------------------
# Runtime Stage: Minimal image
# ------------------------------
FROM debian:bullseye-slim

# Install runtime dependencies for libpq (postgresql-simple needs this to connect to PostgreSQL)
RUN apt-get update && \
    apt-get install -y --no-install-recommends libpq5 && \
    rm -rf /var/lib/apt/lists/*

# Copy the compiled executable from the builder stage
COPY --from=builder /app/bin/your-app-name /usr/local/bin/

# Set the entrypoint to run your app (replace with your actual executable name)
ENTRYPOINT ["your-app-name"]

Key Notes for Your Setup

  • Replace your-app-name: Use the actual executable name defined in your package.yaml or .cabal file.
  • Alpine Alternative: For an even smaller image, use haskell:9.4-alpine as the builder and alpine:3.18 as the runtime. Install postgresql-dev in the builder stage and postgresql-libs in the runtime stage. Note that some Haskell packages may need extra configuration for musl (Alpine's libc), so stick with Debian if you hit issues.
  • stack.lock is Critical: Always commit stack.lock to your repo—it locks dependency versions for consistent builds and helps Docker caching work reliably (the lock file only changes when you update dependencies).
  • Development Speed: For frequent local rebuilds, you can mount your local .stack-work directory as a volume, but use the pure Dockerfile above for production builds to ensure reproducibility.

With this setup, every code change will only trigger a recompile of your application code (not all dependencies), and your final image will be as small as possible—perfect for production deployment.

内容的提问来源于stack exchange,提问作者Leo Zhang

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 04:26:40