Why?
The previous version of the site ran on Hugo — an excellent static site generator. But over time, a few problems accumulated:
-
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.
-
Manual rebuild on every change. Write an article — rebuild and redeploy the entire site. This discourages writing.
-
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 frontmattertemplates/— 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_languagein 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
.envfile. 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
--devmode, a file watcher tracks changes incontent/andtemplates/— the site auto-updates. -
0 warnings.
cargo clippy -- -D warningspasses without a single warning. -
Phusion Passenger. Compatible with Passenger 6 (Generic Language Support).
Passengerfile.jsonwithmax_instances: 1(stateful app). Secure cookie viaX-Forwarded-Proto— correct behavior behind a reverse proxy.
Tech stack
| Component | Technology |
|---|---|
| HTTP server | Axum 0.7 + Tokio |
| Templates | Tera 1.20 |
| Markdown → HTML | pulldown-cmark 0.11 |
| SCSS → CSS | grass 0.13 |
| Configuration | TOML (serde) |
| i18n | Custom system with plural forms |
| OAuth | Google + Yandex (reqwest + HMAC-SHA256 JWT) |
| Database | SQLite (sqlx) — page view counter only |
| Tests | 24 unit + integration tests |
| Docker | 3 compose files (dev/prod/runtime) + zigbuild (glibc 2.17) |
| Deploy | make 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.