Ddmt-Blog Setup and Configuration Guide

· tech · 4 min read · 743 words

Prerequisites

  • A Gitea instance (or any Git host with webhook support)
  • Docker and Docker Compose (recommended), or Bun installed locally
  • An SSH deploy key with read access to your content repository

Quick Start with Docker

The fastest way to get running:

git clone https://github.com/ddmt/Ddmt-Blog.git
cd Ddmt-Blog
cp .env.example .env
# Edit .env with your configuration
docker compose up -d

The blog will be available at http://localhost:4321.

Environment Variables

All configuration is done through environment variables in the .env file.

Site Settings

VariableDefaultDescription
SITE_URLhttp://0.0.0.0:4321Public URL of your blog
SITE_TITLEMy BlogBlog title shown in header
SITE_DESCRIPTIONA blog powered by Bun + AstroDefault meta description
SITE_AUTHORddmtDefault author name
SITE_LOCALEzh-CNHTML lang attribute

Webhook Settings

VariableDefaultDescription
WEBHOOK_SECRETchange-me-in-productionSecret for Gitea webhook signature verification

Git Sync Settings

VariableDefaultDescription
GIT_REPO_URL(empty)SSH URL of your content repository. Leave empty to disable auto-sync.
GIT_DEPLOY_KEY(empty)SSH private key for repository access. Leave empty to disable auto-sync.

Server Settings

VariableDefaultDescription
PORT4321Port the Bun server listens on

Gitea Webhook Configuration

  1. Go to your repository Settings > Webhooks > Add Webhook
  2. Set the target URL to http://your-server:4321/api/webhook
  3. Set the secret to match your WEBHOOK_SECRET
  4. Select “Push events” as the trigger
  5. Content type: application/json

The webhook will trigger a pull and rebuild on every push.

Content Structure

All content lives in the content/ directory:

content/
  posts/2026/05/my-article.md    # Blog posts
  pages/about.md                  # Template pages
  data/friends.yaml               # Structured data
  data/site.json                  # Auto-generated site info
  assets/images/                  # Static images

Writing a Post

Create a Markdown file in content/posts/yyyy/mm/:

---
title: "My New Post"
date: 2026-05-15
description: "A short description for SEO."
category: "tech"
tags: ["tutorial", "web"]
cover: "/assets/images/cover.jpg"
pinned: false
draft: false
---

Your content here...

Frontmatter Fields

FieldRequiredDescription
titleYesPost title
dateYesPublication date
descriptionNoShort description for meta tags
categoryNoPost category (default: “uncategorized”)
tagsNoArray of tags
coverNoCover image URL
pinnedNoPin to top of homepage
draftNoExclude from build

Template Pages

Pages use content from content/pages/ and optional data from content/data/:

  • about.md — Renders the About page
  • friends.md + data/friends.yaml — Renders Friends page with link groups

Friends Data Format

groups:
  - title: "Blogroll"
    links:
      - name: "Example Blog"
        avatar: "https://example.com/avatar.png"
        description: "A great blog about things"
        url: "https://example.com"

Docker Deployment

Dockerfile

The Dockerfile copies the project (excluding dist/, content/, and dev files), installs git, and starts the server. Content is synced and built at startup.

Docker Compose

services:
  blog:
    build: .
    ports:
      - "4321:4321"
    env_file: .env
    volumes:
      - blog-content:/app/content
    restart: unless-stopped

The content/ volume persists your blog content across container restarts. On first start, if GIT_REPO_URL is configured, the container will clone the repository automatically.

Building and Running

docker compose build
docker compose up -d

Logs

docker compose logs -f blog

Manual Deployment (Without Docker)

bun install
# Edit .env
bun run build
bun run start

Reverse Proxy (Nginx)

server {
    listen 80;
    server_name blog.example.com;

    location / {
        proxy_pass http://127.0.0.1:4321;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Troubleshooting

Webhook not triggering rebuilds:

  • Check that WEBHOOK_SECRET matches in both .env and Gitea
  • Verify the webhook URL is reachable from Gitea
  • Check server logs: docker compose logs blog

Content not appearing:

  • Ensure GIT_REPO_URL and GIT_DEPLOY_KEY are set correctly
  • Check that the deploy key has read access to the repository
  • Verify the content directory structure matches the expected layout

Search not working:

  • Pagefind indexes are built at build time
  • Rebuild after adding new content: bun run build