From Hardware to HTTPS: Building My Personal Cybersecurity Blog from Scratch

From Hardware to HTTPS: Building My Personal Cybersecurity Blog from Scratch
Photo by Yannick Pipke / Unsplash

Creating my personal website has been a challenging, educational, and ultimately rewarding experience. What began as a straightforward goal — documenting my learning path in cybersecurity — evolved into a multi-layered project involving hardware assembly, system administration, web development, network configuration, and hands-on troubleshooting.
This entry details every step of how zero-day.is came to life, including both technical decisions and real-world obstacles. I am sharing this not only to document my journey but also to give back to anyone looking to build something similar.

Laying the Foundation: Choosing the Tools

The first crucial decision was to build something that fully reflects my mindset as an aspiring cybersecurity specialist. I wanted complete control over my environment, a deep understanding of every layer, and independence from large hosting providers.

I chose the Raspberry Pi 5 with 8GB RAM as the foundation. It’s a small yet powerful device, capable of hosting a full web application, while remaining energy efficient and quiet enough to run at home 24/7. This choice aligned perfectly with my goal to learn system administration hands-on.

After comparing several blogging platforms, I decided on Ghost for multiple reasons:

  • It has a clean, minimalist interface that aligns with the seriousness of cybersecurity writing.
  • Ghost is open-source, giving me the freedom to customize themes and behavior as needed.
  • The platform is fast and optimized for performance, which is critical even on limited hardware.

Choosing self-hosted Ghost over managed platforms meant I would be fully responsible for every aspect of deployment and security — exactly what I wanted.

Setting Up the Raspberry Pi

When my Raspberry Pi arrived, I began with the basics:

  • Flashed the latest Raspberry Pi OS onto a microSD card using a USB stick and Raspberry Pi Imager.
  • Enabled SSH access by placing an empty ssh file into the boot partition, allowing me to manage the Pi remotely from my main workstation.
  • Connected the Pi to my network via Ethernet to ensure maximum stability, knowing that even minor network issues could disrupt availability.

From there, I could control everything via terminal on my Windows PC. This workflow immediately began to simulate a real-world server management experience.

Additionally, I set a static IP for the Pi within my router settings to avoid issues with dynamic internal IP allocation. This step is essential for reliable port forwarding and internal management.

Preparing the Web Server Environment

Next, I prepared the Raspberry Pi for running production-grade web services. My checklist included:

  • Installing Node.js and npm, as they are core dependencies for Ghost.
  • Setting up MySQL to serve as the backend database.

Database configuration involved:

  • Creating a dedicated database: ghostdb.
  • Setting up a new MySQL user: ghostuser, ensuring it had only the necessary permissions (SELECT, INSERT, UPDATE, DELETE).
  • Securing the MySQL installation by disabling remote root access and removing default test databases.

By following these best practices, I ensured the database would be reliable and secure enough to serve as the backbone of my blog.

Installing and Configuring Ghost

With the environment prepared, I proceeded to install Ghost:

  • Installed Ghost CLI, which greatly simplifies installation and management tasks.
  • Initialized Ghost in /var/www/ghost/, choosing a clean directory to keep the setup organized.

Key configuration points:

  • Set the URL to the future production domain: zero-day.is.
  • Linked Ghost to the ghostdb database.
  • Configured Ghost to listen on 0.0.0.0, enabling external access.
  • Adjusted the Raspberry Pi firewall to open port 2368, Ghost’s default port.

After installation, I tested the local setup by accessing the Pi’s IP and port, confirming that Ghost was running correctly.

To enhance the visual identity of my blog, I selected the Solo theme. It is modern, professional, and perfectly suited for the type of content I planned to publish. I liked its clean lines and simplicity, which places the focus squarely on the written word.

Moving to the Internet: Domain and DNS Configuration

The next milestone was making my site globally accessible.

Because my home IP is dynamically assigned by my ISP, I integrated DuckDNS, a free dynamic DNS service:

  • Created an account and registered a subdomain.
  • Generated my personal DuckDNS token.
  • Wrote an update script for the Raspberry Pi and configured it to run automatically via cronjob every five minutes.

This ensured that my domain would always resolve to my current home IP, even after router reboots or ISP-assigned IP changes.

I also configured my router:

  • Opened ports 80 (HTTP) and 443 (HTTPS), forwarding them to my Raspberry Pi.
  • Confirmed these rules were properly applied and active.

DNS propagation took some time. Using tools like dig and dnschecker.org, I could confirm my DuckDNS domain resolved correctly from various global locations.

Once this step was complete, my server was reachable from anywhere in the world.

Securing the Setup: HTTPS and Firewall Hardening

Running a public-facing server required taking security seriously.

First, I installed UFW, the Uncomplicated Firewall:

  • Allowed only ports 22 (SSH), 80, and 443.
  • Blocked all other inbound traffic.

Next, I set up Fail2Ban to monitor for suspicious activity and automatically block IPs with repeated failed login attempts.

For HTTPS, I used Certbot to generate free Let’s Encrypt certificates:

  • Configured Nginx to serve the Ghost blog via HTTPS.
  • Enabled HTTP to HTTPS redirection to enforce secure access.

Hurdle:
At first, Nginx conflicted with the default site configuration left over from installation. Removing the default site block resolved this cleanly, allowing my domain to serve traffic properly.

With these steps completed, my site was fully encrypted and hardened.

Local Network Access Challenge

Despite external success, I ran into an unexpected issue:
While the site was accessible globally, I couldn’t reach it from devices inside my home network.

Diagnosis:
My router did not support NAT loopback (sometimes called "hairpin NAT"), which prevents devices in the local network from resolving the public domain back to internal resources.

Solution:
I edited the hosts file on my PC and MacBook, manually mapping zero-day.is to my Raspberry Pi’s internal IP address.
This workaround ensured that I could test and develop using my actual domain even from within my own network.

Migrating Offline Blog Content

Before going live, I had already written 15 articles in my offline Ghost instance. Migrating this content was crucial.

I used the Ghost Admin Panel to:

  • Export all my offline content as a .json file.
  • Import this into the live instance.

Hurdle:
Ghost doesn’t perform duplicate checking during imports. Since I had already published some test posts on the live site, the import process resulted in duplicated articles.

Lesson learned:
For clean migrations, it’s better to import into an empty database or clean up beforehand.

Post Display Limitation: Solving the “6 Posts” Problem

After migration, I noticed only six posts were visible on my homepage, even though more were published.

First attempt:
I edited index.hbs and changed the foreach loop to:

handlebarsKopierenBearbeiten{{#foreach posts limit="all"}}

However, this did not solve the issue.

Further investigation revealed:
The number of posts per page is also controlled in the theme’s package.json file under the setting posts_per_page.

Solution:

  • I modified package.json:jsonKopierenBearbeiten"posts_per_page": 100
  • Restarted Ghost using:bashKopierenBearbeitenghost restart
  • Cleared the browser cache to see changes immediately.

Success: All posts were displayed as intended.

Final Results

The project reached completion:

  • zero-day.is is globally accessible, fully encrypted, and performs reliably.
  • The blog shows all my posts, and the Raspberry Pi operates efficiently as a dedicated web server.
  • Firewall and security measures are in place to protect the server.

The satisfaction of seeing my work live on the internet, running on my own hardware, is hard to overstate.

Reflection

This project offered real-world experience in:

  • Linux system administration
  • DNS and dynamic DNS management
  • Web server configuration
  • Security hardening
  • Practical problem-solving

What started as an idea to track my learning has turned into a technical achievement. Beyond just writing, I now understand the backbone of how websites operate at a fundamental level.

Next Steps

With the blog live, I plan to:

  • Automate daily and weekly backups for the database and media files.
  • Optimize performance with caching mechanisms and image compression.
  • Monitor SSL certificate renewals to avoid any service interruptions.

This project taught me that building something from scratch, even with hurdles, is the best way to deeply understand the technologies we rely on daily.
My learning continues, and zero-day.is will grow with me.

Stay tuned.