The problem
Writing content is the easy part.
Publishing it consistently across platforms — with the right metadata, images, tags, canonical URLs, and updates — is where things usually fall apart.
Most workflows still look like this:
- Write content in one tool
- Copy/paste it into WordPress
- Reformat it for another platform
- Forget what was published where
- Lose track of updates and fixes
I wanted something different:
Write once. Publish many. Update safely. Automate everything.
That goal led to building a fully automated, Git-driven publishing pipeline.
The core idea
At the heart of this system is a simple principle:
Markdown in Git is the single source of truth.
Everything else — publishing, updating, tracking, media handling — is derived from that.
The pipeline is designed to be:
- Idempotent (safe to run repeatedly)
- Stateless at the CI level
- State-aware via a database
- Extensible to new platforms
- Ready for containerization and Kubernetes
Current feature set
As of today, the pipeline supports:
Content & metadata
- Markdown with YAML frontmatter
- Title, slug, tags, featured image
- Per-platform publish switches
- Optional
force_updateflag
Publishing
- WordPress publishing via REST API
- Automatic post creation and updates
- Draft or published status
- Automatic canonical URL generation
Featured images
- Uploads media via WordPress REST API
- Reuses existing media (idempotent)
- Declares images directly in Markdown
State tracking
- PostgreSQL-backed state
- Tracks:
- Which platform a post was published to
- Platform-specific post IDs
- Content hashes
- Publication timestamps
- Automatic detection of content changes
Safe updates
- Content hash comparison
- Automatic post updates when Markdown changes
- No duplicate publishing
- No manual DB editing required
The workflow
The publishing flow looks like this:
- Write or edit a Markdown file
- Commit and push to Git
- Jenkins pipeline runs
- The publisher:
- Parses frontmatter
- Computes content hash
- Checks platform state in PostgreSQL
- Uploads featured image if needed
- Creates or updates the WordPress post
- Updates state in the database
- Pipeline finishes — safely and repeatably
Flow diagram
Here is the high-level flow of the system:
{{ diagrams: publishing-flow.png }}
Why this architecture works
A few design decisions make this pipeline robust:
Git is truth
All content and intent live in Git. No hidden state, no UI-only changes.
Database is state
The database tracks what happened, not what should happen. This makes updates and corrections safe.
CI is stateless
Jenkins doesn’t store anything between runs. That makes the system portable and container-friendly.
Platform abstraction
WordPress is just one publisher. Dev.to, Substack, Patreon, or others can be added without changing the core model.
Where this is heading
This pipeline already behaves like a backend service.
The natural next steps are:
- Add more publishing targets (Dev.to, Substack, etc.)
- Introduce analytics and engagement tracking
- Package the publisher as a container
- Run it inside Kubernetes
- Add a simple UI on top
- Offer it as Publishing as a Service
One repo, one workflow, one source of truth — for years of content.
Final thoughts
This started as “I just want to publish more easily”.
It ended up becoming a fully automated publishing platform with:
- Git-based content management
- CI-driven execution
- Database-backed state
- Idempotent, update-safe publishing
And the best part?
Writing content is now the only manual step left. Everything else is automation.
Did you find this post helpful? You can support me.



