ssg

SSG - Static Site Generator

Go Version Go Report Card License CI codecov GitHub Action GitHub issues GitHub stars GitHub Release Docker GitHub forks OpenSSF Scorecard

A fast and flexible static site generator built in Go, designed for simplicity and speed.

Website Installation Documentation Contributing Security

๐Ÿ” Overview

SSG is a static site generator written in Go, optimized for converting WordPress exports (Markdown with YAML frontmatter) to blazing-fast static websites. With its simple architecture, multiple template engine support, and powerful asset pipelines, SSG renders a complete site in milliseconds.

What Can You Build?

SSG is perfect for creating:

Key Capabilities

Feature Description
โšก Lightning Fast Go-powered generation completes in milliseconds
๐ŸŽญ Multiple Engines Go templates, Pongo2 (Jinja2), Mustache, Handlebars
๐ŸŒ Hugo Themes Download and use Hugo themes from GitHub
๐Ÿ–ผ๏ธ Image Pipeline WebP conversion with quality control
๐Ÿ“ฆ Asset Bundling HTML, CSS, JS minification
๐Ÿ”„ Live Reload Built-in server with file watching
๐Ÿณ Docker Ready Minimal Alpine image (~15MB)
๐ŸŽฌ CI/CD Native First-class GitHub Actions support

Development Workflow

Use SSGโ€™s embedded web server during development to instantly see changes to content, structure, and presentation. The watch mode automatically rebuilds your site when files change:

# Start development server with auto-rebuild
ssg my-content krowy example.com --http --watch

Then deploy to any static hosting:

Asset Processing

SSG includes powerful asset processing:

โœจ Features

Core Features

Template Engines

Development

Production

Integration

๐Ÿ“ฆ Requirements

๐Ÿš€ Installation

Quick Install (Linux/macOS)

curl -sSL https://raw.githubusercontent.com/spagu/ssg/main/install.sh | bash

Package Managers

Platform Command
Homebrew (macOS/Linux) brew install spagu/tap/ssg
Snap (Ubuntu) snap install ssg
Debian/Ubuntu wget https://github.com/spagu/ssg/releases/download/v1.3.0/ssg_1.3.0_amd64.deb && sudo dpkg -i ssg_1.3.0_amd64.deb
Fedora/RHEL sudo dnf install https://github.com/spagu/ssg/releases/download/v1.3.0/ssg-1.3.0-1.x86_64.rpm
FreeBSD pkg install ssg or from ports
OpenBSD From ports: /usr/ports/www/ssg

Binary Downloads

Download pre-built binaries from GitHub Releases:

Platform AMD64 ARM64
Linux ssg-linux-amd64.tar.gz ssg-linux-arm64.tar.gz
macOS ssg-darwin-amd64.tar.gz ssg-darwin-arm64.tar.gz
FreeBSD ssg-freebsd-amd64.tar.gz ssg-freebsd-arm64.tar.gz
Windows ssg-windows-amd64.zip ssg-windows-arm64.zip

From Source

git clone https://github.com/spagu/ssg.git
cd ssg
make build
sudo make install

Docker

# Pull image from GitHub Container Registry
docker pull ghcr.io/spagu/ssg:latest

# Run SSG in container
docker run --rm -v $(pwd):/site ghcr.io/spagu/ssg:latest \
    my-content krowy example.com --webp

# Or use docker-compose
docker compose run --rm ssg my-content krowy example.com

# Development server with watch mode
docker compose up dev

๐Ÿ“– Full installation guide: docs/INSTALL.md

๐Ÿ’ป Usage

Syntax

ssg <source> <template> <domain> [options]

Arguments

Argument Description
source Source folder name (inside content-dir)
template Template name (inside templates-dir)
domain Target domain for the generated site

Configuration File

SSG supports configuration files in YAML, TOML, or JSON format. Auto-detects: .ssg.yaml, .ssg.toml, .ssg.json

# Use explicit config file
ssg --config .ssg.yaml

# Or just create .ssg.yaml and run ssg (auto-detected)
ssg

Example .ssg.yaml:

source: "my-content"
template: "krowy"
domain: "example.com"

http: true
watch: true
clean: true
webp: true
webp_quality: 80
minify_all: true

See .ssg.yaml.example for all options.

Options

Configuration:

Option Description
--config=FILE Load config from YAML/TOML/JSON file

Server & Development:

Option Description
--http Start built-in HTTP server (default port: 8888)
--port=PORT HTTP server port (default: 8888)
--watch Watch for changes and rebuild automatically
--clean Clean output directory before build

Output Control:

Option Description
--sitemap-off Disable sitemap.xml generation
--robots-off Disable robots.txt generation
--pretty-html Prettify HTML (remove all blank lines)
--relative-links Convert absolute URLs to relative links
--post-url-format=FMT Post URL format: date (default: /YYYY/MM/DD/slug/) or slug (/slug/)
--minify-all Minify HTML, CSS, and JS
--minify-html Minify HTML output
--minify-css Minify CSS output
--minify-js Minify JS output
--sourcemap Include source maps in output

Image Processing (Native Go - no external tools needed):

Option Description
--webp Convert images to WebP format (requires cwebp)
--webp-quality=N WebP compression quality 1-100 (default: 60)

Deployment:

Option Description
--zip Create ZIP file for Cloudflare Pages

Paths:

Option Description
--content-dir=PATH Content directory (default: content)
--templates-dir=PATH Templates directory (default: templates)
--output-dir=PATH Output directory (default: output)

Template Engine:

Option Description
--engine=ENGINE Template engine: go (default), pongo2, mustache, handlebars
--online-theme=URL Download theme from URL (GitHub, GitLab, or direct ZIP)

Other:

Option Description
--quiet, -q Suppress output (only exit codes)
--version, -v Show version
--help, -h Show help

Shortcodes

Define reusable content snippets in your config file. Each shortcode requires a template file:

shortcodes:
  - name: "thunderpick"
    template: "shortcodes/banner.html"  # Required: template in theme folder
    title: "Thunderpick"
    text: "100% up to $1000 + 5% rakeback"
    url: "https://example.com/promo"
    logo: "/assets/images/thunderpick.png"
    legal: "18+. Gamble Responsibly. T&Cs Apply."

Create the template file (e.g., templates/your-theme/shortcodes/banner.html):

<div class="promo-banner">
  <a href="">
    <img src="" alt="">
    <strong></strong>
    <span></span>
  </a>
  <small></small>
</div>

Use in markdown content with ``:

Check out this amazing offer:



Don't miss it!

Available template variables: ,, ,, ,, ``

Examples

# Development mode: HTTP server + auto-rebuild on changes
./build/ssg my-content krowy example.com --http --watch

# HTTP server on custom port
./build/ssg my-content krowy example.com --http --port=3000

# Generate site with krowy template
./build/ssg krowy.net.2026-01-13110345 krowy krowy.net

# Generate with simple template (dark theme)
./build/ssg krowy.net.2026-01-13110345 simple krowy.net

# Generate with WebP conversion and ZIP package
./build/ssg krowy.net.2026-01-13110345 krowy krowy.net --webp --zip

# Use custom directories
./build/ssg my-content my-template example.com \
  --content-dir=/data/content \
  --templates-dir=/data/templates \
  --output-dir=/var/www/html

# Or using Makefile
make generate        # krowy template
make generate-simple # simple template
make serve           # generate and run local server
make deploy          # generate with WebP + ZIP for Cloudflare Pages

Output

Generated files will be in the output/ folder:

output/
โ”œโ”€โ”€ index.html          # Homepage
โ”œโ”€โ”€ css/
โ”‚   โ””โ”€โ”€ style.css       # Stylesheet
โ”œโ”€โ”€ js/
โ”‚   โ””โ”€โ”€ main.js         # JavaScript
โ”œโ”€โ”€ media/              # Media files
โ”œโ”€โ”€ {slug}/             # Pages and posts (SEO URLs)
โ”‚   โ””โ”€โ”€ index.html
โ”œโ”€โ”€ category/
โ”‚   โ””โ”€โ”€ {category-slug}/
โ”‚       โ””โ”€โ”€ index.html
โ”œโ”€โ”€ sitemap.xml         # Sitemap for search engines
โ”œโ”€โ”€ robots.txt          # Robots file
โ”œโ”€โ”€ _headers            # Cloudflare Pages headers
โ””โ”€โ”€ _redirects          # Cloudflare Pages redirects

๐Ÿ”ง Template Engines

SSG supports multiple template engines. By default, Go templates are used, but you can switch to other engines:

Available Engines

Engine Flag Syntax Style
Go (default) --engine=go .Variable, range .Items
Pongo2 --engine=pongo2 Jinja2/Django: for item in items
Mustache --engine=mustache variable, #items
Handlebars --engine=handlebars variable, #each items

Usage Examples

# Use Pongo2 (Jinja2/Django syntax)
ssg my-content mytheme example.com --engine=pongo2

# Use Mustache
ssg my-content mytheme example.com --engine=mustache

# Use Handlebars
ssg my-content mytheme example.com --engine=handlebars

Online Themes

Download themes directly from GitHub, GitLab, or any ZIP URL:

# Download Hugo theme from GitHub
ssg my-content bearblog example.com --online-theme=https://github.com/janraasch/hugo-bearblog

# Download from any URL
ssg my-content mytheme example.com --online-theme=https://example.com/theme.zip

The theme will be downloaded and extracted to templates/{template-name}/.

Template Syntax Comparison

Go Templates:

{{ range .Posts }}
  <h2>{{ .Title }}</h2>
  <p>{{ .Content }}</p>
{{ end }}

Pongo2 (Jinja2):

{% for post in Posts %}
  <h2>{{ post.Title }}</h2>
  <p>{{ post.Content }}</p>
{% endfor %}

Mustache:

{{#Posts}}
  <h2>{{Title}}</h2>
  <p>{{Content}}</p>
{{/Posts}}

Handlebars:

{{#each Posts}}
  <h2>{{Title}}</h2>
  <p>{{Content}}</p>
{{/each}}

๐ŸŽฌ GitHub Actions

Use SSG as a GitHub Action in your CI/CD pipeline:

Versioning

Reference Description
spagu/ssg@main Latest from main branch (development)
spagu/ssg@v1 Latest stable v1.x release
spagu/ssg@v1.3.0 Specific version

Note: Use @main until a stable release is published.

Basic Usage

- name: Generate static site
  uses: spagu/ssg@main  # or @v1 after release
  with:
    source: 'my-content'
    template: 'krowy'
    domain: 'example.com'

Full Configuration

- name: Generate static site
  id: ssg
  uses: spagu/ssg@v1
  with:
    source: 'my-content'           # Content folder (inside content/)
    template: 'krowy'              # Template: 'simple' or 'krowy'
    domain: 'example.com'          # Target domain
    version: 'latest'              # Optional: SSG version (default: latest)
    content-dir: 'content'         # Optional: content directory path
    templates-dir: 'templates'     # Optional: templates directory path
    output-dir: 'output'           # Optional: output directory path
    webp: 'true'                   # Optional: convert images to WebP
    webp-quality: '80'             # Optional: WebP quality 1-100 (default: 60)
    zip: 'true'                    # Optional: create ZIP for deployment
    minify: 'true'                 # Optional: minify HTML/CSS/JS
    clean: 'true'                  # Optional: clean output before build

- name: Show outputs
  run: |
    echo "Output path: ${{ steps.ssg.outputs.output-path }}"
    echo "ZIP file: ${{ steps.ssg.outputs.zip-file }}"
    echo "ZIP size: ${{ steps.ssg.outputs.zip-size }} bytes"

Action Inputs

Input Description Required Default
source Content source folder name โœ… -
template Template name โœ… simple
domain Target domain โœ… -
version SSG version to download โŒ latest
content-dir Path to content directory โŒ content
templates-dir Path to templates directory โŒ templates
output-dir Path to output directory โŒ output
webp Convert images to WebP โŒ false
webp-quality WebP compression quality 1-100 โŒ 60
zip Create ZIP file โŒ false
minify Minify HTML, CSS, and JS โŒ false
clean Clean output directory before build โŒ false

Action Outputs

Output Description
output-path Path to generated site directory
zip-file Path to ZIP file (if โ€“zip used)
zip-size Size of ZIP file in bytes

Deploy to Cloudflare Pages

name: Deploy

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6

      - name: Generate site
        id: ssg
        uses: spagu/ssg@v1
        with:
          source: 'my-content'
          template: 'krowy'
          domain: 'example.com'
          webp: 'true'

      - name: Deploy to Cloudflare
        uses: cloudflare/pages-action@v1
        with:
          apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
          projectName: 'my-site'
          directory: ${{ steps.ssg.outputs.output-path }}

๐Ÿ“ Complete workflow examples are available in examples/workflows/.

๐Ÿ“ Project Structure

ssg/
โ”œโ”€โ”€ cmd/
โ”‚   โ””โ”€โ”€ ssg/
โ”‚       โ””โ”€โ”€ main.go           # CLI entry point
โ”œโ”€โ”€ internal/
โ”‚   โ”œโ”€โ”€ generator/
โ”‚   โ”‚   โ”œโ”€โ”€ generator.go      # Generator logic
โ”‚   โ”‚   โ”œโ”€โ”€ generator_test.go # Generator tests
โ”‚   โ”‚   โ””โ”€โ”€ templates.go      # Default HTML templates
โ”‚   โ”œโ”€โ”€ models/
โ”‚   โ”‚   โ””โ”€โ”€ content.go        # Data models
โ”‚   โ””โ”€โ”€ parser/
โ”‚       โ”œโ”€โ”€ markdown.go       # Markdown parser
โ”‚       โ””โ”€โ”€ markdown_test.go  # Parser tests
โ”œโ”€โ”€ content/                  # Source data
โ”‚   โ””โ”€โ”€ {source}/
โ”‚       โ”œโ”€โ”€ metadata.json
โ”‚       โ”œโ”€โ”€ media/
โ”‚       โ”œโ”€โ”€ pages/
โ”‚       โ””โ”€โ”€ posts/
โ”œโ”€โ”€ templates/                # Templates
โ”‚   โ”œโ”€โ”€ simple/
โ”‚   โ”‚   โ”œโ”€โ”€ css/
โ”‚   โ”‚   โ””โ”€โ”€ js/
โ”‚   โ””โ”€โ”€ krowy/
โ”‚       โ”œโ”€โ”€ css/
โ”‚       โ””โ”€โ”€ js/
โ”œโ”€โ”€ output/                   # Generated site (gitignored)
โ”œโ”€โ”€ go.mod
โ”œโ”€โ”€ go.sum
โ”œโ”€โ”€ Makefile
โ”œโ”€โ”€ README.md
โ”œโ”€โ”€ CHANGELOG.md
โ”œโ”€โ”€ .gitignore
โ””โ”€โ”€ .dockerignore

๐ŸŽจ Templates

simple - Modern Dark Theme

Elegant dark theme with glassmorphism and gradients:

krowy - Green Farm Theme

Natural light theme inspired by krowy.net:

๐ŸŽจ Styles/Colors

Color Guidelines (WCAG 2.2 Compliant)

Simple Template (Dark)

/* Background */
--color-bg-primary: #0f0f0f;
--color-bg-secondary: #1a1a1a;
--color-bg-card: #222222;

/* Text (minimum contrast 4.5:1) */
--color-text-primary: #ffffff;
--color-text-secondary: #b3b3b3;
--color-text-muted: #808080;

/* Accent */
--color-accent: #6366f1;
--gradient-primary: linear-gradient(135deg, #6366f1 0%, #8b5cf6 50%, #a855f7 100%);

Krowy Template (Light)

/* Background */
--color-bg-primary: #f8faf5;
--color-bg-secondary: #ffffff;
--color-bg-card: #ffffff;

/* Text (minimum contrast 4.5:1) */
--color-text-primary: #1a2e1a;
--color-text-secondary: #3d5a3d;
--color-text-muted: #6b8a6b;

/* Accent */
--color-accent: #2d7d32;
--gradient-primary: linear-gradient(135deg, #2d7d32 0%, #43a047 50%, #66bb6a 100%);

Detailed style documentation: docs/STYLES.md

๐Ÿ—๏ธ Architecture

flowchart TB
    subgraph Input["๐Ÿ“ฅ Input"]
        A[content/source] --> B[metadata.json]
        A --> C[pages/*.md]
        A --> D[posts/**/*.md]
        A --> E[media/*]
    end

    subgraph Processing["โš™๏ธ Processing"]
        F[Parser] --> G[Models]
        G --> H[Generator]
        T[Templates] --> H
    end

    subgraph Output["๐Ÿ“ค Output"]
        H --> I[output/]
        I --> J[index.html]
        I --> K[pages/]
        I --> L[posts/]
        I --> M[category/]
        I --> N[css/]
        I --> O[js/]
        I --> P[media/]
    end

    B --> F
    C --> F
    D --> F
    E --> P

๐Ÿงช Testing

# Run all tests
make test

# Tests with coverage
make test-coverage

# Open coverage report
open coverage.html

๐Ÿ› ๏ธ Development

Available Make Commands

make help           # Show all commands
make all            # deps + lint + test + build
make build          # Build binary
make test           # Run tests
make lint           # Check code
make run            # Build and run
make generate       # Generate site (krowy template)
make generate-simple # Generate site (simple template)
make serve          # Generate and serve locally
make deploy         # Generate with WebP + ZIP for Cloudflare Pages
make clean          # Clean artifacts
make install        # Install binary to /usr/local/bin

Creating Your Own Template

  1. Create a folder in templates/your-template-name/
  2. Add files:
    • css/style.css
    • js/main.js (optional)
    • index.html, page.html, post.html, category.html (optional)
  3. HTML templates are generated automatically if missing

๐Ÿ“„ License

BSD 3-Clause License - see LICENSE

๐Ÿ‘ฅ Authors