WordPress is powerful. Maybe too powerful. If all you want is a clean blog or a product landing page, you quickly find yourself fighting plugin sprawl, sluggish load times, and an admin interface designed for agencies – not for people who just want to write.
I looked around for alternatives and landed on Ghost. Open source, MIT license, modern editor, and most importantly: no overhead. Here is how I set it up.
Running Ghost with Docker
The only prerequisite is Docker – no PHP, no local Node.js, nothing else. A simple docker-compose.yml is all it takes:
services:
ghost:
image: ghost:latest
ports:
- "2368:2368"
environment:
url: http://localhost:2368
database__client: sqlite3
database__connection__filename: /var/lib/ghost/content/data/ghost.db
privacy__useMinimalPasswordStrength: true
volumes:
- ghost_data:/var/lib/ghost/content
- ./themes/my-theme:/var/lib/ghost/content/themes/my-theme
volumes:
ghost_data:
docker compose up -d
Done. Ghost runs on http://localhost:2368, the admin panel at http://localhost:2368/ghost.
One note on the password policy: Ghost enforces a minimum password strength. For local testing, something like Localtest1234 does the job – no special characters needed. And honestly, length beats complexity anyway.
Customizing the Theme
Ghost ships with the Casper theme – minimalist and solid. I used it as a base and copied it directly from the running container:
docker cp $(docker ps -q -f ancestor=ghost):/var/lib/ghost/current/content/themes/casper ./themes/my-theme
The theme now lives locally and is mounted via Docker volume. Changes are reflected immediately.
A Custom Homepage as a One-Pager
By default Ghost shows the blog post list on the homepage. I wanted a product landing page at the top – with the latest posts below it.
Ghost handles this elegantly: if the theme contains a file called home.hbs, it is automatically used for the homepage. Everything else stays untouched.
The trick: don’t hardcode the homepage content into the template. Instead, maintain it as a Ghost Page in the editor – that way it can be updated anytime without touching the theme.
Create a new page in the admin panel with the slug home, write your content, and load it in home.hbs using the {{#get}} helper:
{{!< default}}
{{!-- Cover Image --}}
<div class="site-header-content outer">
{{#if @custom.show_publication_cover}}
{{#if @site.cover_image}}
<img class="site-header-cover"
srcset="{{img_url @site.cover_image size="s"}} 300w,
{{img_url @site.cover_image size="m"}} 600w,
{{img_url @site.cover_image size="l"}} 1000w,
{{img_url @site.cover_image size="xl"}} 2000w"
sizes="100vw"
src="{{img_url @site.cover_image size="xl"}}"
alt="{{@site.title}}"
/>
{{/if}}
{{/if}}
<div class="site-header-inner inner">
<h1 class="site-title">{{@site.title}}</h1>
{{#if @site.description}}
<p class="site-description">{{@site.description}}</p>
{{/if}}
</div>
</div>
{{!-- Load content from the "home" page --}}
{{#get "pages" filter="slug:home"}}
{{#foreach pages}}
<div class="outer">
<h2 class="inner">{{title}}</h2>
</div>
<section class="gh-content gh-canvas">
{{content}}
</section>
{{/foreach}}
{{/get}}
{{!-- Blog Posts --}}
<main id="site-main" class="site-main outer">
<div class="inner posts">
<div class="post-feed">
{{#foreach posts}}
{{> "post-card"}}
{{/foreach}}
</div>
{{pagination}}
</div>
</main>
One important detail: use {{content}} instead of {{{html}}} – only the official Ghost helper correctly renders Full-Width and Wide elements from the editor.
Conclusion
Ghost is what WordPress could have been if it were built today. Clean, fast, and focused. The editor is a pleasure to use, the theme architecture is easy to understand, and Docker makes the whole setup reproducible and portable.
For a personal blog or a product website, I would choose Ghost again without hesitation.
