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:
| 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 --watch
Then deploy to any static hosting:
SSG includes powerful asset processing:
simple (dark) and krowy (green/natural).Variable)--http)--watch)--clean)--webp)--minify-all)--zip)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)
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.
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="">
<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: ,, ,, ,, ``
# 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
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
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=handlebars
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}/.
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}}
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'
- 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"
| 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 |
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/.
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:
#0f0f0f#222222#6366f1 โ #a855f7Natural light theme inspired by krowy.net:
#f8faf5#ffffff#2d7d32/* 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.html
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
templates/your-template-name/css/style.cssjs/main.js (optional)index.html, page.html, post.html, category.html (optional)BSD 3-Clause License - see LICENSE