How can I create a repeatable Ghost development environment using the official Docker image and version‑locked dependencies?
0 reputation · 16 May 2023, 11:24 UTC
0 reputation · 16 May 2023, 11:24 UTC
Goal: establish a Ghost development environment that can be reproduced identically on any machine or CI pipeline by using the official Ghost Docker image and adding custom theme code with fixed Node and npm versions.
Constraints: the solution must avoid manual host‑side Node installations, work across Linux, macOS, and Windows, keep the image size reasonable, and preserve Ghost’s hot‑reload capability during theme edits.
Questions: What is the recommended way to bake a specific Node version into the Ghost Docker image? How can npm lockfiles be persisted across rebuilds without breaking hot reload? Should I extend the official image via a multi‑stage Dockerfile or use a Dockerfile that runs npm ci?
26525 reputation · 16 May 2023, 14:03 UTC
Use a specific tag of the official Ghost Docker image (e.g., ghost:5.46.0) and define a docker‑compose.yml that mounts a ./content volume for persistent data and, if needed, a theme directory. The image already contains a fixed Node.js version, so no additional Node baking is required. Hot‑reload works because Ghost watches files inside the mounted volume.
docker pull ghost:5.46.0
docker‑compose.yml:version: '3.8'
services:
ghost:
image: ghost:5.46.0
restart: unless-stopped
environment:
NODE_ENV: development
GHOST_URL: http://localhost:2368
ports:
- "2368:2368"
volumes:
- ./content:/var/lib/ghost # persistent data, logs, uploads
- ./theme:/var/lib/ghost/content/themes/mytheme # optional: mount your theme
.dockerignore to keep the build context lean (though we are not building an image, it prevents accidental inclusion of node_modules if you later extend the image):
node_modules
npm-debug.log
Dockerfile
.dockerignore
.git
.gitignore
docker compose up -d
docker compose ps
# or
curl -s http://localhost:2368/ghost/api/v3/site/ | jq .version
If your theme requires a build step (e.g., Gulp, Webpack) that must run before Ghost can serve the assets, you will need to either:
In that case, let me know whether you need a build step for your theme so I can adjust the recommendation.
Use comments to ask for clarification. Post a solution as an answer.
26,525 reputation · 16 May 2023, 18:34 UTC
While mounting the theme directory enables hot-reload for template files, it doesn't solve the problem of version-locked dependencies for themes that require a build step (like those using Gulp or Webpack). Since the official Ghost image is optimized for runtime, installing heavy build tools inside it can bloat the image and complicate the environment.
A cleaner approach for repeatable CI or local development is using a sidecar build container in your docker-compose.yml. Define a separate service with a pinned Node image (e.g., node:18-alpine) that shares the same theme volume as the Ghost container:
services:
ghost:
image: ghost:5.46.0
volumes:
- ./theme:/var/lib/ghost/content/themes/mytheme
theme-builder:
image: node:18-alpine
working_dir: /app
volumes:
- ./theme:/app
command: sh -c "npm ci && npm run dev"
This ensures your package-lock.json is strictly honored via npm ci without altering the Ghost runtime image or requiring Node on the host. One caveat: verify the Node major version matches what your theme's toolchain expects, and confirm the built assets land in the shared volume so Ghost picks them up on reload.