How I Automated My Blog with Obsidian + Git

A comprehensive guide to setting up automatic blog deployments using Obsidian, Git, and Astro content collections. Write in Obsidian, push, done.

April 29, 2026 • 7 min read

I’ve been wanting a seamless blogging workflow for years. Something where I could just open my note-taking app, write, and have it automatically appear on my portfolio. After some experimentation, I finally cracked it using Obsidian + Git + Astro.

This post documents the entire setup - from why I chose this approach to the exact configuration details. Let’s dive in.

Auto Blog Setup Overview

The Problem with Traditional Blogging

As developers, we often overengineer things. Writing a blog post shouldn’t require:

  1. Opening VS Code (or any code editor)
  2. Creating a new .md file
  3. Manually typing frontmatter YAML
  4. Running git add, commit, push commands
  5. Waiting for CI/CD to deploy

That’s at least 5 steps every single time. For a lazy developer like me, that’s 5 too many.

What I wanted was:

  • Single app to write in
  • Templates so I don’t forget frontmatter
  • Auto-publish on save
  • Draft mode for work-in-progress posts

The Architecture

Here’s how the pieces fit together:

flowchart LR
    subgraph Writer[" "]
        direction TB
        O[Obsidian]
        T[Template]
        G[Obsidian Git]
    end
    
    O --> T
    T --> G
    
    subgraph Infrastructure[" "]
        direction LR
        GH[GitHub] --> CP[Cloudflare Pages]
        CP --> A[Astro]
    end
    
    G -->|push| GH
    GH -->|deploy| CP
    CP -->|build| A
    
    style Writer fill:#f0f9ff,stroke:#3b82f6,stroke-width:2px
    style Infrastructure fill:#f0fdf4,stroke:#22c55e,stroke-width:2px
    style O fill:#e0f2fe,stroke:#0284c7
    style G fill:#e0f2fe,stroke:#0284c7
    style GH fill:#dcfce7,stroke:#16a34a
    style CP fill:#dcfce7,stroke:#16a34a
    style A fill:#dcfce7,stroke:#16a34a

The stack:

  • Obsidian - Local-first markdown editor
  • Obsidian Git - Auto-commits and pushes changes
  • GitHub - Hosts the repository
  • Astro - Static site generator with content collections

Step-by-Step Setup

Step 1: Configure Obsidian as Your Editor

First, you need to point your Obsidian vault to your blog’s content folder:

  1. Open Obsidian → Open another vault
  2. Navigate to your portfolio’s root folder (not just /src/content/post/)
  3. This is critical - the .git folder must be accessible

The vault structure should look like:

portfolio-v2/
├── .git/
├── src/
│   └── content/
│       └── post/
│           ├── post-1.md
│           ├── post-2.md
│           └── Template.md
└── package.json

Step 2: Install and Configure Obsidian Git

Install the Obsidian Git community plugin, then configure these settings:

SettingValuePurpose
Auto commitONAutomatically save changes
Auto pushONPush to remote immediately
Commit message on timerON (5 min)Fallback if no manual save
Pull on startupONSync changes from other devices

Step 3: Create a Post Template

Create src/content/post/Template.md with all required frontmatter:

---
title: Your Post Title Here
description: A brief summary (1-2 sentences) for the meta description.
dateFormatted: Apr 29, 2026
tags: ["tag1", "tag2"]
published: true
image: your-og-image.png
meta-icon: icon-name
featured: false
---

The key field is published - set it to false while writing, then flip to true when ready.

Step 4: Astro Content Collections Schema

Your posts are validated against this schema in src/content/config.js:

const postCollection = defineCollection({
  type: "content",
  schema: z.object({
    title: z.string(),
    description: z.string(),
    dateFormatted: z.string(),
    tags: z.array(z.string()).optional(),
    image: z.string().optional(),
    banner: z.string().optional(),
    author: z.string().optional(),
    featured: z.boolean().optional(),
    published: z.boolean().optional(),
  }),
});

The published filter ensures unpublished drafts never hit production:

// Only fetch published posts
const allPosts = await getCollection("post", 
  ({ data }) => data.published !== false
);

Advanced Features

Here’s my actual workflow:

sequenceDiagram
    participant You
    participant Obs as Obsidian
    participant Git as Obsidian Git
    participant GH as GitHub
    participant CF as Cloudflare
    
    Note over You,CF: Writing a new post
    
    You->>Obs: Write content (Template.md)
    Note over Obs: Set published: false
    
    You->>Obs: Edit & refine post
    Obs->>Git: Auto-commit (every 5 min)
    Git->>GH: Auto-push to remote
    
    You->>Obs: Set published: true
    Obs->>Git: Commit & push
    Git->>GH: Push changes
    
    GH->>CF: Trigger deployment
    CF->>CF: Build Astro site
    CF-->>You: Post is live!
    
    Note over You,CF: Total time: ~5 minutes

Auto-Generated Open Graph Images

Every post automatically gets an OG image generated using Cloudflare Workers and Bun. The banner includes:

  • Post title
  • Description preview
  • Tags
  • Author name
  • Reading time estimate

OG Image Example

This happens automatically during the build process - no manual work needed.

The related-posts.astro component finds relevant posts by:

  1. Matching tags from current post
  2. Sorting by number of matching tags
  3. Falling back to recent posts if no matches
const relatedPosts = allPosts
  .filter(post => post.id !== currentPostSlug)
  .map(post => {
    const matchingTags = post.data.tags?.filter(
      tag => currentTags.includes(tag)
    );
    return { ...post, matchingTagsCount: matchingTags?.length };
  })
  .sort((a, b) => b.matchingTagsCount - a.matchingTagsCount)
  .slice(0, 3);

Syntax Highlighting

Code blocks use Shiki with a custom theme:

// Example from my actual code
function parseDate(dateStr) {
  const [month, day, year] = dateStr.split(" ");
  return new Date(`${month} ${Number.parseInt(day)}, ${year}`);
}

const sortedPosts = allPosts
  .map(post => ({
    ...post,
    date: parseDate(post.data.dateFormatted),
  }))
  .sort((a, b) => b.date.getTime() - a.date.getTime());

RSS Feed Integration

Every post automatically appears in the RSS feed at /rss.xml:

<item>
  <title>How I Automated My Blog with Obsidian + Git</title>
  <link>https://kodo.sh/post/my-auto-blog-setup</link>
  <description>A comprehensive guide to setting up...</description>
  <pubDate>Tue, 29 Apr 2026 00:00:00 GMT</pubDate>
</item>

Performance Metrics

This setup delivers excellent performance:

MetricScore
Lighthouse Performance98-100
First Contentful Paint~300ms
Time to Interactive<500ms
Total Page Weight<50KB (gzipped)

The secret? Astro ships zero JavaScript for static content by default.

Workflow in Action

Here’s my actual writing process:

  1. Monday 9:00 AM - Open Obsidian, copy Template.md to new-post.md
  2. Monday 9:15 AM - Write content, set published: false
  3. Monday 6:00 PM - Edit, refine, flip published: true
  4. Monday 6:05 PM - Obsidian Git auto-pushes
  5. Monday 6:10 PM - Cloudflare Pages deploys
  6. Monday 6:12 PM - Post is live

Zero friction. No build commands. No remembering YAML schemas.

Troubleshooting

Common issues and fixes:

Posts Not Appearing

  • Check published: true in frontmatter
  • Verify file is in src/content/post/
  • Run pnpm dev to see validation errors

Git Not Committing

  • Confirm vault opens at repo root (has .git folder)
  • Check Obsidian Git plugin is enabled
  • Verify auto-commit settings

Build Errors

  • Frontmatter must be valid YAML
  • Required fields: title, description, dateFormatted
  • Tags must be an array: tags: ["a", "b"]

What’s Next

This setup has been a game-changer. But I’m already thinking about improvements:

  • Automated social posting - Trigger Twitter/LinkedIn posts on publish
  • Analytics integration - Track which posts perform best
  • Newsletter sync - Automatically add to email list
  • AI-assisted writing - Summarize long posts with TL;DR sections

If you want me to cover any of these in a future post, let me know!

Conclusion

The best blogging system is one you’ll actually use. This Obsidian + Git + Astro setup has dramatically lowered the barrier to writing. No more “I’ll post later” - I just write and it’s live.

The total setup time? About 15 minutes. The time saved per post? At least 10 minutes of friction. Plus, the quality of life improvement is massive.

What’s your blogging setup? Would love to hear about different approaches. Drop a comment below or reach out on socials.


Thanks for reading! Want to see more content like this? Subscribe to the RSS feed or follow me on social media.

Share this post