İçeriğe Atla
Mustafa Erbay
Career Written by human · 7 min read · görüntülenme Türkçe oku
100%

A Self-Running Content System: An Indie Hacker's Experience

Problems I hit, lessons I learned, and the small tweaks behind my AI-driven content pipeline. From VPS to GitHub Actions, real field experience.

A Self-Running Content System: An Indie Hacker's Experience — true story cover image

A while ago I set out to automate content creation for my own side projects, including this blog. The goal was an AI-driven content pipeline that runs without my manual intervention. A system that creates posts, edits them, publishes them, and even SEO-optimizes them. The technical challenges I faced, the lessons I took, and the practical know-how I picked up while bringing this system up are exactly the kind of thing that happens to an “indie hacker.”

In this post I’ll walk through what I went through step by step while building this “self-running content system,” what problems I wrestled with, and how I solved them. As a system architect, my expectations on these projects run high — but seeing how things actually work in practice is always a different experience.

Why Did I Build a Self-Running System?

I have multiple side products like hesapciyiz.com, spamkalkani.com and islistesi.com. I know each one needs regular content but I have limited time. That keeps me asking “how can I do more?” That was the moment a need emerged: scale content production and increase consistency.

Producing content by hand is both very time-consuming and a serious bottleneck for a one-person operation like mine. Looking at where AI technology had reached, the idea of handing this load over to automation was appealing. That way I could focus more on writing code and optimizing systems.

The Pipeline’s Early Steps and Early Mistakes

I built the foundation with Astro, handle some backend work with Node.js, use SQLite as the database and serve via Nginx. I manage services with systemd and automate CI/CD with GitHub Actions. Cloudflare provides the caching and security layer. On paper everything looked great.

But initially, when I piled everything on my own VPS, performance issues showed up fast. One day at 6 a.m. I got a “DEGRADED” mail from my Pipeline-health monitor. When I connected, I saw kcompactd had hit 92% CPU; even sshd couldn’t accept new connections. I think that aligned with the moment I had written sleep 360, got OOM-killed and the system was trying to recover. I watched swap explode and memory get used down to the last drop.

After that incident, I decided to move some heavy workloads off my VPS to GitHub Actions. Running build and AI inference steps there made sense. But I quickly started exceeding GitHub Actions’ free quota. That pushed me toward a self-hosted runner. Setting up a GitHub Actions runner on my own VPS was an economical way to avoid blowing through the quota.

While setting up the self-hosted runner, I ran into its own problems. Runner state corruption hit me many times; especially the pain of manually deleting directories under _work/_temp. Sometimes runners would misbehave, fail to take jobs, or get stuck. That showed me how critical reliability is in an automation system. Automation that constantly needs manual intervention isn’t really automation.

AI Integration and Unexpected “Quirks”

AI models are at the heart of the content system. I take the raw text from these models, process it, convert it into MDX format and integrate it into the blog. AI models often produce great results, but unexpected “quirks” sometimes appear.

For example, AI putting a slash (/) character in the tags field, or returning the publishDate field as a quoted string. Astro’s frontmatter parser would error on inputs like that. Another interesting issue was around the Turkish i character; sometimes a different encoding was used instead of plain i. Those small details forced manual fixes and broke my automation flow.

# Example of cleaning frontmatter from AI output
import re

def clean_frontmatter(frontmatter_str):
    # Replace slash in tags with comma
    frontmatter_str = re.sub(r'tags:\s*\[([^\]]+)\]', 
                            lambda m: "tags: [" + m.group(1).replace('/', ', ') + "]", 
                            frontmatter_str)
    # Strip quotes from publishDate
    frontmatter_str = re.sub(r'publishDate:\s*"(\d{4}-\d{2}-\d{2})"', r'publishDate: \1', frontmatter_str)
    # Fix dotted-i character issue (e.g. when 'İ' arrives instead of 'İ')
    frontmatter_str = frontmatter_str.replace('İ', 'İ').replace('i̇', 'i')
    return frontmatter_str

# Example usage
# cleaned_fm = clean_frontmatter(ai_generated_frontmatter)

I had to build a “parser” layer to handle this kind of issue. The parser holds a set of rules that standardize incoming text, fix errors, and make it MDX-compatible. That way, no matter what comes from AI, it lands in the format my system expects. For reliability I implemented the preflight resource guard pattern; before starting an operation, I check whether all the needed resources (disk, memory, API credits, etc.) are available. Then, in error situations, auto-fix mechanisms kick in, and the dedup-alert pattern prevents me from getting multiple alerts for the same error.

Disk Management and Docker Experiences

I run more than 13 Docker containers on my own VPS. Postgres, Redis, Next.js applications and pieces of this blog are among these containers. If one of them misbehaves, the whole system drops into swap and performance falls off a cliff. That means a constant need to monitor disk and memory.

On April 28, I woke up to find my disk was 100% full. The root cause was Docker’s accumulated build caches and unused images. 33 GB of build cache and 23 GB of unused images were taking up serious space on disk. That reminded me once again how important running docker system prune -a regularly is. By automating that command, I prevented the disk-fire scenario.

Similarly, my Astro build process sometimes caused unexpected memory issues. On a VPS with 7.6 GB of RAM, the Astro build could go OOM (Out Of Memory) by eating 2.5 GB of RAM. That affected the reliability of my build pipeline. For these situations, I had to break the build process into smaller steps and optimize resource usage. For example, pulling image optimization into a separate step or tuning parallel processing limits.

Reliability and Cloudflare Strategies

Automation isn’t just about things running, it’s about them running reliably. So I set up error handling and retry mechanisms at every step of the pipeline. If something goes wrong, the system needs to self-heal — or at least alert me correctly.

Cloudflare plays an important role in this system. It manages the blog’s traffic, provides DDoS protection, and of course caching. But Astro returning max-age=0 by default conflicted with Cloudflare’s caching strategies. That meant content was being requested from the server on every request, causing performance drops. I solved that by manually overriding the Cache-Control header in my Nginx config.

# Override Cloudflare cache control via Nginx
location / {
    # Ignore Astro's max-age=0 and apply our own cache rules
    add_header Cache-Control "public, max-age=3600, stale-while-revalidate=600";
    # Other Nginx config...
    try_files $uri $uri/ /index.html;
}

These fine-tuning steps show that automation systems aren’t just code — they have to be thought through together with the infrastructure and network layers. To monitor every step of the pipeline and proactively detect issues, I also set up my own monitoring with tools like Prometheus and Grafana. That way, instead of saying “something broke,” I can act on concrete data like “memory usage exceeded 80% on service X.”

Conclusion

Building a self-running content system brought far more technical detail and fine-tuning than I expected. The experience I gained as an “indie hacker” doesn’t end with writing code; it spans server management, network configuration, automation reliability and debugging.

I took a lesson from every mistake and made the system tougher step by step. The VPS going OOM, Docker filling the disk, the GitHub Actions runner getting corrupted, or the AI producing strange characters… These were all concrete examples that a system is only as strong as its weakest link. I’m in such a process right now and continue to optimize. Maybe in a future post I’ll explain how this system updates the financial calculators too.

Paylaş:

Bu yazı faydalı oldu mu?

Yükleniyor...

Bu yazı nasıldı?

ME

Mustafa Erbay

Sistem Mimarisi · Network Uzmanı · Altyapı, Güvenlik ve Yazılım

2006'dan bu yana sistem mimarisi, network, sunucu altyapıları, büyük yapıların kurulumu, yazılım ve sistem güvenliği ekseninde çalışıyorum. Bu blogda sahada karşılığı olan teknik deneyimlerimi paylaşıyorum.

Kişisel Notlar

Bu notlar sadece sizde saklanır. Tarayıcınızda yerel olarak tutulur.

Hazır 0 karakter

Comments

Server-side AI Moderation

Comments are AI-moderated server-side and stored permanently.

?
0/2000

Server-side AI moderation

✉️ Free · No spam · Unsubscribe anytime

Curated digest, hand-picked by me — not the AI

Once a week: the most important post of the week, behind-the-scenes notes, and a "what I actually used this week" section. Less noise, more signal.

  • 📌
    Best of the week Single most-worth-reading post
  • 🔧
    Toolbox notes Real tools I used this week
  • 🧠
    Behind-the-scenes Notes that don't make it to blog

We don't spam. Unsubscribe anytime. · Tracked only by Umami (self-hosted, no Google).

Your Reading Stats

0

Posts Read

0m

Reading Time

0

Day Streak

-

Favorite Category

Related Posts