Building a lightweight, high-performance blog with Deno and server-side rendering

How to build a fast personal website with Deno, Hono, server-rendered HTML, and vanilla CSS.

Welcome to my personal website. After years of building frontend architectures for millions of users, I designed this site to be fast, simple, and reliable without frontend framework bloat.

The philosophy: zero client-side frameworks

Modern web development has gotten heavy. For content-driven platforms and personal engineering journals, shipping megabytes of JavaScript just to render static markup is unnecessary overhead.

This blog runs entirely on the server. The browser receives clean semantic HTML and minimal CSS, leading to instant page loads and zero client-side hydration delays.

"Simplicity is prerequisite for reliability." - Edsger W. Dijkstra

The core stack

The tech stack focuses on speed, simplicity, and low maintenance. Each piece has a single, well-defined job:

  • Runtime: Deno runs TypeScript natively with built-in security, standard libraries, and zero build step.
  • HTTP routing: Hono handles routing, request context, and caching headers with a tiny footprint.
  • Templates: Eta provides lightweight, fast server-side HTML rendering.
  • Content parsing: Marked and Speed Highlight transform Markdown files into highlighted HTML on the server.
  • Styling: Vanilla CSS using custom properties for theming, with zero client-side JavaScript libraries.

Organizing the code: a bit of fun overengineering

Let's address the elephant in the room: this setup uses ports and adapters (hexagonal architecture). For a personal blog, that is definitely overkill.

This blog is also my sandbox - a free zone where I can practice software patterns and overengineer on purpose just for the fun of it.

The structure separates business rules from external libraries:

  • Core: Contains the Post entity and interfaces (PostRepository, TemplateRender, MarkdownParser). It has no knowledge of Deno APIs, Hono, or disk access.
  • Adapters: Implement those interfaces using concrete tools like FilePostRepository, EtaTemplateRender, and MarkedParser.
  • Main: The entry point ties the pieces together and starts the server.
const template = new EtaTemplateRender();
const markdownParser = new MarkedParser();

const filePostRepository = new FilePostRepository(markdownParser);
const postService = new PostService(filePostRepository);

const server = createRoutes(template, postService);

Deno.serve({ port: 4000 }, server.fetch);

It keeps the code tidy and modular, even if a couple of flat files would have worked just fine.

Server-side markdown rendering

Articles live in the repository as Markdown files with YAML frontmatter. When the server boots or loads a post, the parser reads the content and processes code blocks directly on the server.

Syntax highlighting happens during the parsing step using @speed-highlight/core. This eliminates client-side syntax highlighting scripts completely. The browser receives pre-styled markup that renders immediately.

Deployment pipeline with Docker and Dokploy

The application deploys automatically using a lightweight Alpine-based Docker container.

FROM denoland/deno:alpine

WORKDIR /app

COPY deno.json deno.lock* ./
COPY . .

RUN deno install --entrypoint main.ts
USER deno

EXPOSE 4000
CMD ["run", "--allow-net", "--allow-read", "--allow-env", "main.ts"]

The build caches dependencies and runs under a non-root deno user for security.

Hosting runs on a self-hosted Dokploy instance. Whenever I push changes to the main branch, Dokploy detects the commit, rebuilds the Docker image, and restarts the container with zero manual intervention.

Edge caching and proxy with Cloudflare

Cloudflare sits in front of the application as a reverse proxy and CDN. It provides automatic SSL, DDoS mitigation, and global edge caching.

The Hono application configures HTTP caching headers for production:

  • Static assets (/assets/*) receive a one-year immutable cache header.
  • Rendered pages receive a 14-day cache header with stale-while-revalidate.

When a user visits the site, Cloudflare serves the cached page from the nearest edge location. The origin server only handles cache misses or fresh background updates, keeping response times under 50ms worldwide.

What comes next

I will continue writing about software engineering, frontend architecture, and developer tooling. Upcoming topics include:

  1. Frontend architecture at scale: Design systems, state management, and microfrontends.
  2. UX engineering and craft: Bridging user research, typography, and frontend performance.
  3. Open source and tooling: Lessons learned building developer tools and indie applications.

Stay tuned, and feel free to connect on GitHub or LinkedIn.

Action completed