-
Copy the environment file and start all services:
cp .env.example .env docker compose up -d
This starts three services: db (MySQL), ghost (Ghost 6), and dev (Node 22 running Vite watch).
-
Wait ~30 seconds for MySQL to initialize:
docker compose logs -f db
Wait until you see "ready for connections".
-
Wait for the dev service to install dependencies and start watching:
docker compose logs -f dev
Wait until you see "watching for file changes".
-
Visit http://localhost:2368/ghost and complete the Ghost setup wizard (create your admin account).
-
Go to Settings > Design > Change theme > Activate theme-one (or whichever theme you want to work on).
-
Start Docker services (if not already running):
docker compose up -d
The dev service automatically runs
pnpm dev(Vite watch on all themes). -
Edit
.hbsfiles — Ghost's native livereload detects changes and auto-refreshes the browser. -
Edit
.css/.jsfiles — Vite (in the dev container) rebuildsassets/built/, then Ghost livereload picks up the new files and refreshes the browser. -
Monitor builds:
docker compose logs -f dev
-
Copy an existing theme:
cp -r themes/theme-one themes/your-theme-name
-
Update
nameinthemes/your-theme-name/package.json:"name": "your-theme-name"
-
Add volumes in
docker-compose.ymlunder the ghost service:# Theme bind mount - ./themes/your-theme-name:/var/lib/ghost/content/themes/your-theme-name # Hide node_modules from Ghost's chown - /var/lib/ghost/content/themes/your-theme-name/node_modules
-
Restart all services to pick up the new bind mount and workspace package:
docker compose down && docker compose up -d -
Activate the theme in Ghost Admin: Settings > Design > Change theme.
gscan is Ghost's official theme validator. It checks your theme against the Ghost theme specification.
# Validate a specific theme
pnpm --filter theme-one lint
# Validate all themes
pnpm lint
# Run directly with verbose output
cd themes/theme-one && npx gscan . --verbose- Required template files exist (default.hbs, index.hbs, post.hbs, etc.)
- Handlebars helpers are used correctly
- Package.json has required fields (
name,version,engines.ghost) - Assets are referenced properly
- 0: No errors (warnings may still be present)
- Non-zero: Errors found — theme will be rejected by Ghost
| Issue | Fix |
|---|---|
Missing error.hbs |
Create an error template with {{statusCode}} and {{message}} |
Missing {{ghost_head}} |
Add {{ghost_head}} before </head> in default.hbs |
Missing {{ghost_foot}} |
Add {{ghost_foot}} before </body> in default.hbs |
Invalid {{content}} usage |
Use {{content}} inside a {{#post}} block |
# Build a specific theme
pnpm --filter theme-one build
# Build all themes
pnpm buildThe build output lands in each theme's assets/built/ directory. These files are what Ghost serves to visitors.