19 Years of WordPress — Why I Finally Quit
wordpress,php,symfony,static-site,performancePart 1 of 4
holas.pl ran on WordPress from 2007 to early 2026. Nineteen years. In that time WordPress grew from a straightforward blogging platform into something I barely recognize — and maintaining it had become more work than maintaining the actual content.
This is the first post in a series documenting the migration to a custom Symfony-based static site generator. The next posts cover the architecture, the contact form, the developer workflow, and building the site with AI assistance.
The Update Treadmill
WordPress ships security updates frequently. Plugins ship updates independently. Themes do too. On a quiet week my update queue had three items. On a bad week it had fifteen. Each one required:
- Back up the database and files
- Apply the update
- Check that nothing broke
That last step is the killer. WordPress plugins interact with each other in ways that are impossible to predict. An update to a caching plugin would break the SEO plugin's output. An update to the SEO plugin would change how sitemaps were generated. Every update was a small gamble.
For a portfolio site that publishes a post every few months, this maintenance overhead is absurd.
Dozens of Plugins for Basic Features
A fresh WordPress installation does almost nothing useful. To run a respectable portfolio blog you need plugins for:
- SEO — Yoast or Rank Math, with their own settings screens, meta boxes, and update cycles
- Caching — W3 Total Cache or WP Super Cache, with complex configuration and edge cases
- Security — Wordfence or similar, scanning for intrusions, blocking IPs, sending daily reports
- Contact form — Contact Form 7 or Gravity Forms
- Backups — UpdraftPlus or similar, usually pushing to an external service
- Performance — image optimization, lazy loading, minification
- SMTP — because WordPress sends email through PHP
mail()by default, which goes to spam
Each plugin is code you don't control, maintained by someone else, potentially abandoned tomorrow, running on every page load.
When I started planning the migration I counted 22 active plugins.
Bot Attacks and Login Problems
A WordPress login page is a known target. The URL is always /wp-admin/ or /wp-login.php. Every bot on the internet knows this. The result: constant brute-force login attempts, around the clock.
I had Wordfence rate-limiting login attempts, blocking suspicious IPs, and sending me daily attack reports. It worked — but it was another moving part consuming server resources on a site that didn't need user logins at all. The only person logging in was me, once a month.
XML-RPC was another attack vector. WordPress enables it by default for pingbacks and the mobile app. Bots probe it constantly. The fix was an nginx rule to block it — but you only find out about it after seeing the traffic in your logs.
PHP and MySQL on a Raspberry Pi
holas.pl runs on self-hosted hardware — first a Banana Pi, now a Raspberry Pi 5. These are capable ARM boards, but they are not server-grade machines. Running PHP-FPM and MySQL simultaneously for a blog that changes once a month is wasteful.
A typical WordPress page load on that hardware: PHP parses the request, MySQL runs several queries to fetch post content, navigation data, sidebar widgets, and site options, PHP renders templates, every active plugin executes its hooks. All of this for content that was identical to the last request and will be identical to the next one.
With a static site, nginx serves a pre-rendered HTML file directly from disk. The ARM processor barely wakes up. Pages load faster than WordPress could parse the incoming request.
The Database as a Liability
Nineteen years of WordPress produces a database with thousands of rows of post metadata, options, post revisions, and transients. It needs:
- Regular backups (and testing that restores actually work)
- Occasional cleanup — WordPress accumulates garbage: post revisions, expired transients, orphaned metadata rows
- Monitoring for corruption after unclean shutdowns
- A MySQL process running continuously, consuming memory
A portfolio blog is a collection of text and images. Storing it in a relational database adds operational complexity without adding value.
What I Kept
The content. All 26 posts were exported and converted to Markdown files — titles, dates, categories, tags, images. The URLs were preserved exactly: 25 individual post redirects and 43 image path redirects are now baked into the nginx config. Search rankings survived the migration intact.
Everything else — the database, the plugins, the update queue, the /wp-admin/ login page — is gone.
The next post covers what replaced it: a custom Symfony application that generates static HTML files and serves them through nginx, with no PHP involved in delivering content to visitors.