How This Blog Works in 2026
Source: Dev.to
Overview
Back in 2019 I wrote about how this blog works. A lot has changed since then — automated deploys, dev.to sync, AI‑generated tweets. Time for an update.
Tech Stack
- Grav CMS – flat‑file, PHP 8.2, Markdown, no database.
- Deployer 7.3 – handles the actual deploy (symlink strategy, shared dirs for cache, automatic cleanup of old releases).
- GitLab CI – orchestrates the whole pipeline.
Theme
The blog theme extends Quark and adds custom styles: alternating white/gray section backgrounds, a tag cloud with CSS custom properties, and article cards. All JavaScript is pure vanilla.
Bilingual Setup
The site is bilingual – Czech (*.cs.md) and English (*.en.md). Every article has both files side by side.
Deployment Pipeline
# .gitlab-ci.yml (excerpt)
stages:
- build
- deploy
build_image:
stage: build
script:
- docker build -t myblog:ci -f Dockerfile.ci .
only:
changes:
- Dockerfile.ci
deploy:
stage: deploy
script:
- dep deploy
only:
- master
- Push to
mastertriggers the pipeline. - Build image – Docker image with PHP 8.2 and Deployer (only when
Dockerfile.cichanges). - Deploy –
dep deployover SSH tobiberle.cz.
The SSH key is stored as a base64 CI variable, decoded at runtime. The entire deploy is fully automatic.
dev.to Synchronization
-
Export: English articles are automatically published to dev.to.
- A sync script scans all English articles, matches them against existing dev.to posts via
canonical_url, and creates or updates as needed. - Stateless – the canonical URL serves as the key.
- Runs in CI after every deploy and on a schedule (for future‑dated articles that become publishable).
- A sync script scans all English articles, matches them against existing dev.to posts via
-
Import: dev.to articles can be imported back into the local blog, downloading cover images and setting
canonical_urlback to the blog.
Twitter Automation
When a new article goes live, it is automatically tweeted. The tweet text is generated by OpenAI (gpt‑4o‑mini) following a writing‑style guide.
Process
- Scan all English articles.
- Skip already‑tweeted articles (tracked list).
- For new articles, send the title, excerpt, and tags to OpenAI.
- OpenAI returns a tweet (max 250 chars) matching the author’s tone.
- Post the tweet via Twitter API v2 with OAuth 1.0a.
- Record the article URL and commit the update back to Git.
The style guide lives in a single file, providing a single source of truth for both the AI and the author. Tweets are sent only on weekdays at 09:00 AM (Europe/Prague) via a separate GitLab CI schedule with the TWITTER_POST=true variable.
Things That Remain Unchanged
- Grav remains perfect for a personal blog – fast, simple, minimal maintenance.
- Markdown + Git continues to be the best workflow for writing technical articles.
- Deployer does exactly what it should and nothing more.
- Umami analytics replaces Google Analytics (the template placeholder is already there).
- Better cover images for articles.
Future Ideas
- Add an RSS feed for Czech articles.
Automation is great, but it’s useless without articles. So, write more.