↓ Skip to main content
  1. Posts/

Creating static website from obsidian notes

·1233 words·6 mins·

On this post, I will be demonstrating quick and easy way of creating static websites from your obsidian notes. This is possible with Hugo which is an open-source framework for static site generation.

info

Hugo basically turns markdown files to html, so you can still follow along even if you don’t use obsidian and skip the obsidian related parts.

Since I am on arch, some commands may not work for you. If that is the case refer the original documentation.

Prerequisites for Hugo:

Install Hugo:

sudo pacman -S hugo

Create new site using Hugo (this creates the site under a folder):

hugo new site blog

Initialize git repo:

cd blog/
git init

Download a theme (I am using Blowfish, this is optional but recommended:

git submodule add -b main https://github.com/nunocoracao/blowfish.git themes/blowfish

Follow the theme documentation and make necessary changes for your website.

To test the site:

hugo server --disableFastRender --noHTTPCache

Since we are using Obsidian, we need to copy notes from the obsidian folder to the website folder.

tip

I placed notes that I will use for my website under a new folder called posts in the obsidian vault.

We can achieve this with the following rsync command:

rsync -av --delete "src-path" "dest-path"

If there is an image on the note, it will not be parsed but only the wikilink for the image will be shown. We need to copy the image to the website folder.

First we need to create an images folder under the static folder.

mkdir static/images

Then we can run this python script and copy the images to our newly created images folder. Replace paths for your directories and make sure that they are correct otherwise the script won’t work. Create this script as images.py.

import os
import re
import shutil

# Paths
posts_dir = "your-post-dir"
attachments_dir = "your-image-dir"
static_images_dir = "your-static-website-image-dir"
base_url_path = ""  # Empty for root-hosted site 


# Step 1: Process each markdown file in the posts directory
for filename in os.listdir(posts_dir):
    if filename.endswith(".md"):
        filepath = os.path.join(posts_dir, filename)
        
        with open(filepath, "r") as file:
            content = file.read()
        
        # Step 2: Find all image links in Obsidian format ![Image Description](/images/image.png)
        images = re.findall(r'!\[\[([^]]*\.png)\]\]', content)
        
        # Step 3: Replace image links and ensure URLs are correctly formatted
        for image in images:
            # Prepare the Markdown-compatible link with %20 replacing spaces
            markdown_image = f"![Image Description]({base_url_path}/images/{image.replace(' ', '%20')})"
            content = content.replace(f"![[{image}]]", markdown_image)
            
            # Step 4: Copy the image to the Hugo static/images directory if it exists
            image_source = os.path.join(attachments_dir, image)
            if os.path.exists(image_source):
                shutil.copy(image_source, static_images_dir)

        # Step 5: Write the updated content back to the markdown file
        with open(filepath, "w") as file:
            file.write(content)

print("Markdown files processed and images copied successfully.")
warning

Run rsync command before running the image script.

Run the script:

python3 images.py

You should see the images being copied from your obsidian vault to the static/images folder.

We need to create 2 more files. These will be used for creating custom deployment configuration on Cloudflare Workers.

Create a wrangler.toml file at the root of your website folder, then paste the following content:

name = "blog" # use the same name from the workers page
compatibility_date = "2026-03-01" # change this to current date

[build]
command = "./build.sh"

[assets]
directory = "./public"
not_found_handling = "404-page"

Create a new script called build.sh, again at the root of your website folder and paste this:

#!/usr/bin/env bash
main() {
    HUGO_VERSION=0.153.3 # change this to your hugo version

    export TZ="Europe/Istanbul" # change this to your timezone

    # Install Hugo
    echo "Installing Hugo v${HUGO_VERSION}..."
    curl -LJO https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.tar.gz
    tar -xf "hugo_extended_${HUGO_VERSION}_linux-amd64.tar.gz"
    cp hugo /opt/buildhome
    rm LICENSE README.md hugo_extended_${HUGO_VERSION}_linux-amd64.tar.gz

    # Verify installed version
    echo "Verifgying Hugo installation..."
    echo Go: "$(go version)"
    echo Hugo: "$(hugo version)"
    echo Node: "$(node --version)"

    # Clone themes repository
    echo "Cloning themes repository - archie..."
    git submodule update --init --recursive
    git config core.quotepath false

    # Build the site
    echo "Building the site..."
    hugo --gc --minify
}

set -euo pipefail
main "$@"

Make this script executable:

chmod +x build.sh

Before using Cloudflare workers make sure that you have the latest version of your site available in your remote repo.

Now that we are done with copying our notes from obsidian to website folder, we can publish it under our domain using Cloudflare Workers Platform.

After logging in to Cloudflare, go to Build -> Compute & AI -> Workers & Pages. Click New Deployment on top-right and select github and finish authorization. Select the website repo and click deploy. This may take some time or the build may fail. If there are any errors, look at the logs and press re-build after fixing the error and pushing the fixed version to the remote repo.

If the build is complete then congrats your site is ready to be used. As the last step, you can configure custom domain to point the site to your own domain name. Go to Settings -> Add -> Custom Domain and enter your domain there.

Image Description

Now you can visit your domain and see your static website.

As a bonus this is the script that combines the process of copying notes, images from the obsidian vault and pushing changes to the remote repo. Create this script at the root of your website folder, you can name it whatever you want, make it executable and run it.

Don’t forget to change directory paths and the repo address.

#!/bin/bash
set -euo pipefail

# Change to the script's directory
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"

# Set variables for Obsidian to Hugo copy
sourcePath="src-path"
destinationPath="dest-path"

# Set GitHub Repo
myrepo="your-remote-repo-address"

# Check for required commands
for cmd in git rsync python3 hugo; do
    if ! command -v $cmd &> /dev/null; then
        echo "$cmd is not installed or not in PATH."
        exit 1
    fi
done

# Step 1: Check if Git is initialized, and initialize if necessary
if [ ! -d ".git" ]; then
    echo "Initializing Git repository..."
    git init
    git remote add origin $myrepo
else
    echo "Git repository already initialized."
    if ! git remote | grep -q 'origin'; then
        echo "Adding remote origin..."
        git remote add origin $myrepo
    fi
fi

# Step 2: Sync posts from Obsidian to Hugo content folder using rsync
echo "Syncing posts from Obsidian..."

if [ ! -d "$sourcePath" ]; then
    echo "Source path does not exist: $sourcePath"
    exit 1
fi

if [ ! -d "$destinationPath" ]; then
    echo "Destination path does not exist: $destinationPath"
    exit 1
fi

rsync -av --delete "$sourcePath" "$destinationPath"

# Step 3: Process Markdown files with Python script to handle image links
echo "Processing image links in Markdown files..."
if [ ! -f "images.py" ]; then
    echo "Python script images.py not found."
    exit 1
fi

if ! python3 images.py; then
    echo "Failed to process image links."
    exit 1
fi

# Step 4: Add changes to Git
echo "Staging changes for Git..."
if git diff --quiet && git diff --cached --quiet; then
    echo "No changes to stage."
else
    git add .
fi

# Step 5: Commit changes with a dynamic message
commit_message="New Blog Post on $(date +'%Y-%m-%d %H:%M:%S')"
if git diff --cached --quiet; then
    echo "No changes to commit."
else
    echo "Committing changes..."
    git commit -m "$commit_message"
fi

# Step 6: Push all changes to the main branch
echo "Deploying to GitHub Main..."
if ! git push origin main; then
    echo "Failed to push to main branch."
    exit 1
fi

echo "All done! Site synced, processed, committed, built, and deployed."

References:

Mert Ergun
Author
Mert Ergun
Tech nerd | Cybersecurity | IT