Introduction
I have received so many messages asking how I built this website that I finally stopped deflecting and wrote it all down.
The short answer is: GitHub Pages, Jekyll, Markdown, and some CSS. No WordPress, no hosting bills, no plugin update treadmill. I started this way because I wanted to focus on writing, not maintenance — and years later I am still happy with that decision.
Before this, I had websites built in PHP and on WordPress. Both work, but both demand attention: security patches, plugin conflicts, database backups, and hosting renewals. Once you step off that treadmill, it is hard to go back.
My current setup gives me:
- Free hosting on GitHub Pages
- Git-based versioning — every draft, every revision, easy rollback
- Markdown for content — clean, portable, no lock-in
- Full control over HTML, CSS, and JavaScript when I want it
- AI art and AI writing tools for illustrations and drafts
- A form service that delivers contact and subscription requests to my inbox with built-in spam filtering
In this post, I will walk you through the whole thing, from creating a GitHub account to custom domains, SEO, and the no-code alternatives worth knowing about.
Steps for creating a website on GitHub
Here is the overall flow, which I will explain in detail below:
- Set up a GitHub account
- Create a new repository named
username.github.io - Choose a static site generator (we will use Jekyll)
- Write content in Markdown
- Customise the look and feel
- Test locally; push changes
- Enable GitHub Pages
- Optionally: connect your own domain name
The official starting point is Creating a GitHub Pages site with Jekyll, but I will go further here.
A GitHub Pages site
GitHub Pages hosts static websites directly from your repository. When you push a change, GitHub builds the site automatically using Jekyll and serves the result — no server to manage, no database to back up.
You write your posts in Markdown, and Jekyll turns them into HTML. Markdown is so minimal that most people are productive with it in under an hour.
Two ways to create your Jekyll blog
You can build entirely in the GitHub web interface (no tools to install) or run Jekyll locally for a faster iteration loop. I will cover both, starting with the simpler approach.
The simplest way — edit everything in the browser
Set up a GitHub account
GitHub is a platform for version-controlled collaboration. It also happens to offer free static site hosting. If you are not a developer, do not let the word “version control” put you off — you will get the hang of it quickly, and it will save you from losing work more than once.
I have a few posts about Git if you want to go deeper on the versioning side. For now, just create a free account at github.com.
Create a new repository
A GitHub repository is where your project lives: posts, images, templates, and configuration. To publish a GitHub Pages site, name the repository exactly username.github.io, replacing username with your GitHub username.
- Click the
+icon in the top right corner → New repository - Name it
username.github.io - Initialise it with a README
That name is important: it signals to GitHub that this repository should be served as a website at https://username.github.io.
What is Jekyll?
Jekyll is a static site generator written in Ruby. It takes your Markdown files and Liquid templates and produces a folder of plain HTML that can be served from anywhere — including GitHub Pages, which has built-in Jekyll support.
Key features that make it worth learning:
| Feature | What it does |
|---|---|
| Markdown support | Write posts in plain text; Jekyll converts to HTML |
| Liquid templating | Variables, loops, conditionals in your layouts |
| Front matter | YAML metadata at the top of each file (title, date, tags…) |
| Themes | Pre-built layouts you can apply to the whole site |
| GitHub Pages integration | GitHub builds and deploys automatically on every push |
| Fast and secure | Static HTML — no server-side code, no database to exploit |
Create _config.yml
The _config.yml file is Jekyll’s control panel. Create it in the root of your repository:
# _config.yml
title: Your Website Title
description: A brief description of your website
url: "https://your-username.github.io"
baseurl: "" # Keep empty for a root-level GitHub Pages site
repository: your-username/your-repo-name
branch: main
Commit this file directly through the GitHub interface (“Add file” → “Create new file” → name it _config.yml).
Write content in Markdown
Create a file called index.md for your homepage. Markdown files use simple syntax that renders as HTML:
# My First Blog Post
Welcome to my blog! In this post, I'll share my thoughts on **Markdown** and its simplicity.
## What is Markdown?
Markdown is a lightweight markup language that lets you format text without HTML.
### Why use Markdown?
- **Easy to learn**: simple, readable syntax
- **Versatile**: headings, lists, links, code blocks, images
- **Portable**: works on GitHub, Notion, VS Code, and everywhere else
Blog posts go in a _posts/ directory. The filename must follow this pattern: YYYY-MM-DD-title.md.
Commit your changes
After writing or editing a file in the GitHub interface, scroll to the bottom, enter a short commit message describing what you changed (“Add homepage” or “New post on Flask”), and click Commit changes. That is all — GitHub Pages will pick up the change within seconds.
Enable GitHub Pages
- Go to your repository’s Settings tab
- Scroll to the Pages section (left sidebar or main content area, depending on GitHub’s current layout)
- Under Source, select your branch (
main) and folder (/ (root)) - Click Save
Your site will be live at https://username.github.io shortly after.
Edit content online
The GitHub interface is perfectly usable for ongoing writing. I used it heavily in the early days. Once your posts pile up, a local setup becomes more efficient — but there is no rush.
A local setup — test before you publish
When you want to preview changes before the world sees them, install Jekyll locally. The official guide is Testing your GitHub Pages site locally with Jekyll.
1. Install Jekyll
You will need Ruby and RubyGems first. On macOS, the easiest route is via Homebrew:
brew install ruby
gem install jekyll bundler
On Windows, use the RubyInstaller for Windows.
2. Clone your repository
git clone https://github.com/username/username.github.io.git
cd username.github.io
You may need a personal access token if you use HTTPS — see my post The Token Way to GitHub Security.
3. Install dependencies
bundle install
This installs all gems listed in your Gemfile, including the exact Jekyll version GitHub Pages uses.
4. Run the local server
bundle exec jekyll serve
Always use bundle exec — it ensures Jekyll runs with the exact gem versions in your Gemfile.lock, not whatever happens to be globally installed. Visit http://127.0.0.1:4000/ to see a live preview that rebuilds on every file save.
5. Push to publish
When you are happy with the result:
git add .
git commit -m "Add new blog post"
git push origin main
GitHub Pages rebuilds automatically within a few seconds of the push.
Bonus material
These are the things people ask about most once the basics are working.
IDE — what to write in
Any text editor works. These are the ones I recommend or have used:
Visual Studio Code — my current recommendation for anyone new to this stack. Free, excellent Markdown preview, strong Git integration built in, and extensions for Jekyll, Liquid, and YAML syntax highlighting. The GitHub Repositories extension even lets you edit files directly from your browser without cloning.
PyCharm — what I use daily for Python work. It handles Markdown editing well and has solid Git integration. Not specifically a Markdown editor, but comfortable if you are already in that ecosystem.
Typora — a dedicated Markdown editor with a clean live-preview interface. Paid, but reasonable. Great for distraction-free writing.
Zed — fast, modern, collaborative editor gaining traction in 2024–2025. Worth trying if you want something lightweight and snappy.
Note: GitHub’s Atom editor was officially retired on 15 December 2022. If you still have it installed, it will not receive security updates — switch to VS Code or one of the alternatives above.
Jekyll patterns worth knowing
Jekyll’s folder structure does specific jobs. Understanding these five patterns will cover most of what you need:
1. Layouts (_layouts/) — define the HTML shell that wraps your content. Posts typically use a post layout, pages use page or default. You write your content in Markdown; Jekyll slots it into the right layout automatically.
_layouts/
├── default.html
├── post.html
└── page.html
2. Includes (_includes/) — reusable HTML snippets. Navigation, footers, affiliate link components, social sharing buttons — anything you want to drop into multiple pages with a single Liquid tag.
_includes/
├── header.html
├── footer.html
└── navigation.html
3. Data files (_data/) — YAML, JSON, or CSV files that act as a lightweight database. I use a tools.yml file to store all the AI tool metadata that powers my tool finder widget, for example.
_data/
├── tools.yml
└── settings.json
4. Collections — group related content that is not quite “blog posts.” Portfolio items, courses, series — anything with its own permalink structure.
# _config.yml
collections:
portfolio:
output: true
permalink: /portfolio/:title/
5. Permalinks — control your URL structure globally in _config.yml or per-page in front matter. I use /blog/:year/:month/:day/:title/ for post URLs, which keeps them readable and date-scoped.
# In _config.yml
permalink: /blog/:year/:month/:day/:title/
Run the local server after making structural changes:
bundle exec jekyll serve
GitHub repository tips
Name the repository username.github.io for a root-level site. If you use a different name, the site will live at username.github.io/repo-name and you will need to set baseurl in _config.yml accordingly.
Standard daily workflow:
# Pull the latest changes (if working from multiple machines)
git pull origin main
# Stage, commit, and push
git add .
git commit -m "Add new blog post"
git push origin main
Automate with GitHub Actions (optional)
GitHub Pages now has native deployment support via actions/deploy-pages, which is the cleanest option for most Jekyll sites in 2024 and beyond. Go to Settings → Pages → Source → GitHub Actions to use it. GitHub will suggest a starter workflow for Jekyll automatically.
If you prefer a manual workflow (for more control), here is an updated, working example:
# .github/workflows/jekyll.yml
name: Deploy Jekyll site to GitHub Pages
on:
push:
branches:
- main
permissions:
contents: read
pages: write
id-token: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.3'
bundler-cache: true # Runs bundle install and caches gems automatically
- name: Build Jekyll site
run: bundle exec jekyll build
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
deploy:
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: $
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
This uses the current action versions (checkout@v4, ruby/setup-ruby@v1, upload-pages-artifact@v3, deploy-pages@v4) and the native GitHub Pages deployment model, which is more reliable than the older peaceiris/actions-gh-pages approach.
Own domain
A custom domain (like daehnhardt.com) replaces the default username.github.io address. You buy it from a registrar — I recommend Namecheap or Cloudflare Registrar (Cloudflare sells at cost with no markup).
Note: Google Domains was sold to Squarespace in 2023 and is now called Squarespace Domains. If you have a domain there it still works, but for new purchases I would look elsewhere.
Full instructions are in Managing a custom domain for your GitHub Pages site. The short version:
1. Add A records in your registrar’s DNS settings:
| Type | Name | Value |
|---|---|---|
| A | @ | 185.199.108.153 |
| A | @ | 185.199.109.153 |
| A | @ | 185.199.110.153 |
| A | @ | 185.199.111.153 |
2. Add a CNAME record for www:
| Type | Name | Value |
|---|---|---|
| CNAME | www | username.github.io |
3. Set the custom domain in GitHub Pages settings — go to Settings → Pages → Custom domain, enter your domain, and save.
4. Enable Enforce HTTPS — GitHub Pages provisions a free Let’s Encrypt certificate automatically.
DNS changes can take minutes to propagate or occasionally a few hours. If something looks wrong after 24 hours, check Troubleshooting custom domains and GitHub Pages.
HTML templates and styling
When Markdown is not enough — a custom layout, a widget, a special section — you can drop into HTML directly. Jekyll processes Liquid tags inside HTML files, so you get the same template power as in Markdown posts.
A minimal HTML page:
<!DOCTYPE html>
<html lang="en">
<head>
<title>My Web Page</title>
</head>
<body>
<h1>Welcome to My Web Page</h1>
<p>This is a sample paragraph.</p>
<img src="image.jpg" alt="An example image">
<a href="https://www.example.com">Visit Example.com</a>
</body>
</html>
CSS (Cascading Style Sheets) controls how that HTML looks — fonts, colours, spacing, layout:
/* styles.css */
body {
font-family: Arial, sans-serif;
background-color: #f4f4f4;
color: #333;
}
h1 {
color: #007bff;
}
p {
font-size: 16px;
line-height: 1.5;
}
img {
max-width: 100%;
height: auto;
}
I wrote more about this in AI-free Website Design, including some ChatGPT prompts for generating layouts and beginner-friendly CSS tips.
HTML web form submissions
Jekyll generates static HTML — there is no server-side code to handle a contact form or subscription request. For that you need a third-party form service.
I use UseBasin.com and it has been rock-solid. You get:
- Submissions delivered by email
- Spam filtering and CAPTCHA support
- Auto-reply configuration
- Zapier integration for routing submissions to Slack, Google Sheets, or hundreds of other apps
UseBasin.com also offers a drag-and-drop form builder if you prefer not to touch HTML. I customise the HTML directly — the setup is minimal:
<form action="https://usebasin.com/f/my_form_id" method="POST">
<label for="email">Email:</label>
<input type="email" id="email" name="email">
<input type="submit" value="Submit">
</form>
Replace my_form_id with the ID from your UseBasin dashboard.
AI-generated art
The cover images on this blog are almost all AI-generated — mostly Midjourney and, more recently, FLUX.1 [schnell] running locally on my Mac.
Some posts to read if you want to add AI images to your blog:
- Mastering Midjourney Prompts for Stunning Images
- From Dutch Golden Age to AI Art: A Journey with Vermeer and AI
Getting organic traffic
I will be honest: SEO is a long game. My approach has never been to chase algorithms — it has been to write things I would have wanted to find when I was learning. The traffic follows eventually, unevenly, and sometimes from posts I least expected.
That said, here are the things that genuinely make a difference:
- Keyword research before you write — not to contort your prose, but to find out how people phrase the questions your post answers. Google Search Console is free and shows you exactly what queries bring people to your site.
- Title and meta description — these are what appear in search results. Make them specific and honest.
- Internal links — link between your own posts where it makes sense. This helps readers and crawlers alike.
- Mobile-friendly design — Google has been mobile-first since 2019. Jekyll’s responsive themes handle this well by default.
- Page speed — static sites are fast by nature. The main culprits for slowdowns are unoptimised images. Run your images through Squoosh before adding them to the repository.
- Consistency — publishing regularly matters more than publishing perfectly. One solid post a month beats a burst of ten followed by silence.
I have written more about the specifics in:
SEO in Jekyll front matter
Jekyll makes it easy to add per-page SEO metadata. In your layout’s <head> section, pull in front matter variables:
<title>How I Built This Blog — and How You Can Too | Living with AI, Elena's blog</title>
<meta name="description" content="I get asked how I built this blog more often than almost anything else. Here is the honest answer — GitHub Pages, Jekyll, Markdown, a sprinkle of HTML, and a form service that keeps spam out. No WordPress, no server bills, full control.">
<meta name="keywords" content="How to host with GitHub Pages, GitHub Pages with Jekyll, AI-powered websites, GitHub Pages, UseBasin form integration, static website with forms, Blogging with GitHub Pages, Markdown website, Jekyll blog setup">
Then in each post’s front matter:
title: "My Post Title"
excerpt: "A clear one-sentence summary for search engines."
keywords: "keyword one, keyword two, keyword three"
Keep excerpt under 160 characters — that is the length search engines display in results.
No code
If HTML and Jekyll feel like too much, these tools let you build a blog or website without touching code.
AI website builders
Mixo.io generates a full website — content, design, SEO structure — from a text prompt. It also handles mobile-friendly design and on-page SEO automatically. I wrote about it in Creating Websites with AI on Mixo.io.
10web.io is another AI-powered option, particularly strong if you want WordPress under the hood without managing it yourself. It can recreate any existing website and lets you customise content and images from there.
Blogging platforms
Blogger (Blogspot) — owned by Google, free, integrates with other Google services. Good starting point if you want zero setup.
Medium — clean reading experience, built-in audience discovery. Your content copyright stays with you, but you grant Medium a broad licence to distribute it on their platform. Worth reading the terms before publishing anything you plan to monetise elsewhere.
Wix — drag-and-drop website builder with a free tier. More flexible than Blogger, less technical than GitHub Pages.
WordPress — the dominant CMS on the web. Hosted WordPress.com is maintenance-free; self-hosted WordPress.org gives you full control at the cost of running a server. If your goal is a simple blog, the overhead may not be worth it compared to GitHub Pages. I moved away from it and have not looked back.
Conclusion
GitHub Pages with Jekyll has been the right tool for this blog for years. It is free, fast, Git-backed, and gets out of my way. The learning curve is real — Git and Markdown take a bit of time to settle in — but once they do, publishing a new post feels light.
If the technical setup is not for you, the no-code tools I mentioned above have matured considerably. There is genuinely good tooling at every point on the spectrum now.
Let me know if you have questions or run into trouble with your setup — I am happy to help.
Did you like this post? Please let me know if you have any comments or suggestions.
Posts about building websites and SEO that might be interesting for youRelated tools you may want to try next.
B12.io Recently, I have found an AI-powered platform that enables you to create professional websites, pages, posts, and emails with ease. I will also give it a try and soon write a new post about B12.io (I am working on my coding post at the moment :).
Fotor AI uses text-to-video generator technology that allows you to easily create, manage, and streamline cinematic AI avatar videos.
Hey Gen uses text-to-video generator technology that allows you to easily create, manage, and streamline cinematic AI avatar videos.
Hour One AI uses text-to-video generator technology that allows you to easily create, manage, and streamline cinematic AI avatar videos.
Pictory.ai creates professional quality videos from your script with realistic AI voices, matching footage and music in a few clicks. Pictory.AI can also convert blog posts into captivating videos and extract highlights from your recordings to create branded video snippets for social media, and much more.
References
1. Creating a GitHub Pages site with Jekyll
2. Testing your GitHub Pages site locally with Jekyll
3. The Token Way to GitHub Security
4. Troubleshooting custom domains and GitHub Pages
6. Managing a custom domain for your GitHub Pages site
11. Squoosh — image optimisation tool