Why?

The previous version of the site ran on Hugo — an excellent static site generator. But over time, a few problems accumulated:

  1. PHP dependency for OAuth and contacts. Hugo generates static HTML, while dynamic features (Google/Yandex login, contacts form) required a separate PHP backend. Two runtimes — double the complexity.

  2. Manual rebuild on every change. Write an article — rebuild and redeploy the entire site. This discourages writing.

  3. Code duplication. PHP shortcode processing and Hugo templates lived in separate worlds but essentially did the same thing.

I wanted something more cohesive. One binary containing everything: the blog, authentication, and static file serving.

Why Rust?

I've been writing Rust in my free time and enjoy its approach: zero-cost abstractions, memory safety without a garbage collector, and an excellent web ecosystem (Axum, Tera, pulldown-cmark).

A Rust server starts in milliseconds, uses minimal memory, and keeps all content in memory — no database, no caches, no external services.

What came out of it?

One binary (~25 MB) + directories = the whole site:

  • content/ — Markdown files with TOML frontmatter
  • templates/ — Tera templates (like Hugo layouts)
  • static/ — static assets (CSS, JS, fonts, SVG)
  • sass/ — SCSS (compiled at startup via grass)
  • i18n/ — translations (with plural form support)
  • config.toml — configuration
  • .env — OAuth keys

No PHP. No rebuilds. Edit a .md file in --dev mode — the site updates instantly via hot reload.

Key features

  • Dynamic rendering. The server reads Markdown at startup and serves HTML on the fly. No static generation — pages are built at request time.

  • Two languages out of the box. Russian and English. default_language in config controls URL prefixes: / for the primary language, /en/ for others.

  • Split shortcodes. Two prefixes: {scp:...} for pages (requires explicit opt-in in frontmatter), {sct:...} for templates (always active). OAuth buttons in header and contacts in about are implemented as short calls with parameters and language support.

  • OAuth without PHP. Google and Yandex authentication directly from the Rust server. Keys from .env file. CSRF protection via constant-time comparison (subtle::ct_eq).

  • RSS and Sitemap. Three sitemap modes: index, per-language, unified with hreflang. RSS 2.0 with <![CDATA[...]]>. SHA-256 ETags for conditional requests (304 Not Modified).

  • Generation time in footer. Every page shows render time: microseconds if under 1 ms, milliseconds with decimals otherwise.

  • Complete hiding of hidden pages. Pages with hidden = "true" are excluded everywhere: section listings, RSS, sitemap, tags, direct access (404). Even hidden section assets (images) are blocked.

  • Hot reload. In --dev mode, a file watcher tracks changes in content/ and templates/ — the site auto-updates.

  • 0 warnings. cargo clippy -- -D warnings passes without a single warning.

  • Phusion Passenger. Compatible with Passenger 6 (Generic Language Support). Passengerfile.json with max_instances: 1 (stateful app). Secure cookie via X-Forwarded-Proto — correct behavior behind a reverse proxy.

Tech stack

ComponentTechnology
HTTP serverAxum 0.7 + Tokio
TemplatesTera 1.20
Markdown → HTMLpulldown-cmark 0.11
SCSS → CSSgrass 0.13
ConfigurationTOML (serde)
i18nCustom system with plural forms
OAuthGoogle + Yandex (reqwest + HMAC-SHA256 JWT)
DatabaseSQLite (sqlx) — page view counter only
Tests24 unit + integration tests
Docker3 compose files (dev/prod/runtime) + zigbuild (glibc 2.17)
Deploymake dist → build.zip + Phusion Passenger

All fits into ~9500 lines of Rust code (35 files).

Result

The site is fast, compiles cleanly, has no external runtime dependencies, and is easy to deploy. Writing articles became simpler: save a Markdown file — and changes are live instantly.

The binary cross-compiles for glibc 2.17 via cargo zigbuild — works on CentOS 7+, Debian 8+, and any modern Linux. Deployment is a single build.zip (13 MB) or a Docker image.

This was a fun experience. I highly recommend it to anyone considering rewriting their pet project in Rust — it's worth it.