A fast and flexible static site generator built in Go, designed for simplicity and speed.
Website | Installation | Documentation | Contributing | Security
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.
SSG is perfect for creating:
- οΏ½ Blogs - Personal or professional blogs migrated from WordPress
- π’ Corporate sites - Fast, secure company websites
- π Documentation - Technical docs with clean SEO URLs
- π¨ Portfolios - Image galleries and creative showcases
- π Landing pages - Marketing and product pages
- π Personal sites - Resumes, CVs, and personal branding
| 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 |
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 --watchThen deploy to any static hosting:
- Cloudflare Pages - Zero-config with our example workflow
- GitHub Pages - Direct push deployment
- Netlify, Vercel - Drag and drop or Git integration
- Any web server - Just copy the output folder
SSG includes powerful asset processing:
- Image Processing - Convert JPG/PNG to WebP with configurable quality
- HTML Minification - Remove whitespace, comments, optimize output
- CSS Minification - Bundle and compress stylesheets
- JS Minification - Optimize JavaScript files
- SEO Automation - Sitemap, robots.txt, clean URLs, meta tags
- π Fast generation - Go-powered, millisecond builds
- π Markdown - Full support with YAML frontmatter
- π¨ Built-in templates -
simple(dark) andkrowy(green/natural) - π± Responsive - Mobile-first design
- βΏ Accessible - WCAG 2.2 compliant
- π SEO - Clean URLs, sitemap, robots.txt
- π§ Go Templates - Default, powerful templating (
.Variable) - π Pongo2 - Jinja2/Django syntax (for loops, filters)
- π¨β𦱠Mustache - Logic-less templates (sections)
- π¨ Handlebars - Semantic templates (each blocks)
- π HTTP Server - Built-in dev server (
--http) - π Watch Mode - Auto-rebuild on changes (
--watch) - π Config Files - YAML, TOML, JSON support
- π§Ή Clean Builds - Fresh output (
--clean)
- πΌοΈ WebP Conversion - Optimized images (
--webp) - ποΈ Minification - HTML, CSS, JS (
--minify-all) - π¦ Deployment Package - Cloudflare Pages ready (
--zip) - π³ Docker - Multi-arch Alpine image
- π¬ GitHub Actions - Use as CI/CD step
- π Online Themes - Download Hugo themes from URL
- π WordPress - Import from WP exports
- Go 1.25 or later
- Make (optional, for Makefile)
cwebp(optional, for WebP conversion)
curl -sSL https://raw.githubusercontent.com/spagu/ssg/main/install.sh | bash| 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 |
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 |
git clone https://github.com/spagu/ssg.git
cd ssg
make build
sudo make install# 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
ssg <source> <template> <domain> [options]| Argument | Description |
|---|---|
source |
Source folder name (inside content-dir) |
template |
Template name (inside templates-dir) |
domain |
Target domain for the generated site |
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)
ssgExample .ssg.yaml:
source: "my-content"
template: "krowy"
domain: "example.com"
http: true
watch: true
clean: true
webp: true
webp_quality: 80
minify_all: trueSee .ssg.yaml.example for all 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 |
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="{{.URL}}">
{{if .Logo}}<img src="{{.Logo}}" alt="{{.Name}}">{{end}}
<strong>{{.Title}}</strong>
<span>{{.Text}}</span>
</a>
{{if .Legal}}<small>{{.Legal}}</small>{{end}}
</div>Use in markdown content with {{shortcode_name}}:
Check out this amazing offer:
{{thunderpick}}
Don't miss it!Available template variables: {{.Name}}, {{.Title}}, {{.Text}}, {{.URL}}, {{.Logo}}, {{.Legal}}, {{.Data.key}}
# 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 PagesGenerated 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
SSG supports multiple template engines. By default, Go templates are used, but you can switch to other 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 |
# 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=handlebarsDownload 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.zipThe theme will be downloaded and extracted to templates/{template-name}/.
Go Templates:
{% raw %}
{{ range .Posts }}
<h2>{{ .Title }}</h2>
<p>{{ .Content }}</p>
{{ end }}{% endraw %}
Pongo2 (Jinja2):
{% raw %}
{% for post in Posts %}
<h2>{{ post.Title }}</h2>
<p>{{ post.Content }}</p>
{% endfor %}{% endraw %}
Mustache:
{% raw %}
{{#Posts}}
<h2>{{Title}}</h2>
<p>{{Content}}</p>
{{/Posts}}{% endraw %}
Handlebars:
{% raw %}
{{#each Posts}}
<h2>{{Title}}</h2>
<p>{{Content}}</p>
{{/each}}{% endraw %}
Use SSG as a GitHub Action in your CI/CD pipeline:
| 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
@mainuntil a stable release is published.
- name: Generate static site
uses: spagu/ssg@main # or @v1 after release
with:
source: 'my-content'
template: 'krowy'
domain: 'example.com'{% raw %}
- 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"{% endraw %}
| 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 |
| 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 |
{% raw %}
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 }}{% endraw %}
π Complete workflow examples are available in
examples/workflows/.
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
Elegant dark theme with glassmorphism and gradients:
- Dark background:
#0f0f0f - Cards:
#222222 - Accent: purple gradient
#6366f1β#a855f7 - Hover animations and micro-interactions
Natural light theme inspired by krowy.net:
- Light background:
#f8faf5 - Cards:
#ffffff - Accent: green
#2d7d32 - Cow icon π in logo
- Nature and ecology focus
/* 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%);/* 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
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
# Run all tests
make test
# Tests with coverage
make test-coverage
# Open coverage report
open coverage.htmlmake 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- Create a folder in
templates/your-template-name/ - Add files:
css/style.cssjs/main.js(optional)index.html,page.html,post.html,category.html(optional)
- HTML templates are generated automatically if missing
BSD 3-Clause License - see LICENSE
- spagu - GitHub