Docker Build-Time vs Runtime: The Post-Install Hook Pattern

πŸ“– 2 minutes read

Here’s a pattern I use in nearly every Docker project: create scripts at build time, execute them at runtime.

The Problem

Some things can’t happen during docker build. Maybe you need environment variables that only exist at runtime. Maybe you need to run migrations against a database that isn’t available yet. Maybe you need to generate config files from templates.

The instinct is to shove everything into the entrypoint script. But then your entrypoint becomes a 200-line monster that’s impossible to debug.

The Pattern

Split it into two phases:

# Build time: COPY or CREATE the scripts
COPY docker/post-install/*.sh /docker-entrypoint.d/
RUN chmod +x /docker-entrypoint.d/*.sh
#!/bin/bash
# entrypoint.sh β€” Runtime: execute the hooks
for f in /docker-entrypoint.d/*.sh; do
    echo "Running post-install hook: $f"
    bash "$f"
done

exec "$@"

Why This Works

Each hook is a single-purpose script. 01-generate-config.sh renders templates from env vars. 02-run-migrations.sh handles database setup. 03-create-cache-dirs.sh ensures directories exist with correct permissions.

You can test each hook independently. You can add new ones without touching the entrypoint. And if one fails, the error message tells you exactly which hook broke.

The Key Insight

Build time is for things that are static β€” installing packages, copying files, compiling assets. Runtime is for things that depend on the environment β€” config generation, service discovery, data setup.

The hook directory pattern bridges the two. Your Dockerfile prepares the hooks. Your entrypoint runs them. Clean separation, easy debugging.

If you’ve used the official Nginx or PostgreSQL Docker images, you’ve already seen this pattern β€” they use /docker-entrypoint-initdb.d/ for the exact same reason.

Daryle De Silva

VP of Technology

11+ years building and scaling web applications. Writing about what I learn in the trenches.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *