How This Website Is Created

2016-02-19

Abstract

A self-hosted website setup using LaTeX, Pandoc, Docker, and Git. Pages are plain HTML, version-controlled, and published via containers without dynamic components.

Introduction

This document outlines the infrastructure and publishing method for the static site hosted at eipi.dev. All infrastructure is based on open tools and containers (Merkel, 2014). Each page is written in LaTeX, converted to HTML using Pandoc (MacFarlane, 2006–2025), and served through a containerized PHP webserver behind a Cloudflare Tunnel. The entire stack is orchestrated with Docker (Docker Inc., 2025).

Method

Page Creation

Each page was written as a .tex file in plain LaTeX, with minimal metadata.

\title{Some Title}
\date{2025-11-19}
\begin{document}
\maketitle
...
\end{document}

Conversion was done using:

pandoc -s input.tex -o output.html

Repository Setup

A bare Git repository was initialized in the shared container volume:

cd /var/www/html
git init --bare .git

A post-receive hook was created to check out the working tree:

#!/bin/sh
GIT_WORK_TREE=/var/www/html git checkout -f

The hook was saved to .git/hooks/post-receive and made executable.

SSH Access

Key generation was done with:

ssh-keygen -t ed25519 -C "eipi.dev git access"

The public key was pasted into the PUBLIC_KEY environment variable in the Compose file.

Index Page Automation

The file index.php automatically scans all numbered HTML pages, extracts the <title> tag, and displays a sorted list.

<?php
$pages = glob("*.html");
usort($pages, function($a, $b) {
    return (int)$b - (int)$a;
});

echo "<ul>\n";
foreach ($pages as $file) {
    if (!ctype_digit(pathinfo($file, PATHINFO_FILENAME))) continue;

    $contents = file_get_contents($file);
    if (preg_match("/<title>(.*?)<\/title>/i", $contents, $matches)) {
        $title = htmlspecialchars($matches[1]);
        echo "  <li><a href=\"$file\">$title</a></li>\n";
    }
}
echo "</ul>\n";
?>

The result is a standard unordered list of links, sorted with the highest-numbered file first. Only files with numeric filenames are included.

Tunnel Setup and Remote Access

To enable secure remote access, a Cloudflare Tunnel is used. This creates an outgoing encrypted connection from the local server to Cloudflare’s network. The tunnel client runs in a container with the command:

cloudflared tunnel run

A ‘TUNNEL_TOKEN‘ is passed via environment variable, which is generated from the Cloudflare dashboard and associated with a preconfigured tunnel. The domain (e.g. eipi.dev) is routed through Cloudflare and linked to the tunnel endpoint.

cloudflared:
  image: cloudflare/cloudflared:latest
  command: tunnel run
  environment:
    - TUNNEL_TOKEN=YOUR_CLOUDFLARE_TUNNEL_TOKEN
  volumes:
    - tunneldata:/etc/cloudflared

This makes the entire container network accessible via Cloudflare, without exposing ports to the public internet.

SSH via Tunnel

SSH access is exposed through the same Cloudflare Tunnel. A specific subdomain (e.g. git.eipi.dev) is routed to the SSH container using Cloudflare’s configuration. This is done with:

cloudflared tunnel route dns <TUNNEL_ID> git.eipi.dev

And in the tunnel’s configuration file (e.g. config.yml):

tunnel: <TUNNEL_ID>
credentials-file: /etc/cloudflared/<TUNNEL_ID>.json

ingress:
  - hostname: git.eipi.dev
    service: ssh://localhost:2222
  - service: http_status:404

The SSH container is configured to listen on port 2222. Once routed, Git can push directly to:

git remote add origin ssh://[email protected]:/var/www/html

The SSH public key was added to the PUBLIC_KEY environment variable in the Compose file, and access is authenticated via key-based login.

Containerization

All services were containerized with Docker Compose. Volumes were shared across containers, with the website source mounted at /var/www/html in both the webserver and SSH containers. The following services were used:

Compose File

networks:
  eipi_net:

services:
  web:
    image: php:8.2-apache
    networks:
      - eipi_net
    volumes:
      - webdata:/var/www/html
    restart: unless-stopped

  ssh:
    image: lscr.io/linuxserver/openssh-server:latest
    environment:
      - USER_NAME=git
      - PUBLIC_KEY=ssh-ed25519 AAAAC3... user@host
    networks:
      - eipi_net
    volumes:
      - sshdata:/config
      - webdata:/var/www/html
    restart: unless-stopped

  cloudflared:
    image: cloudflare/cloudflared:latest
    command: tunnel run
    networks:
      - eipi_net
    environment:
      - TUNNEL_TOKEN=YOUR_CLOUDFLARE_TUNNEL_TOKEN
    volumes:
      - tunneldata:/etc/cloudflared
    restart: unless-stopped

volumes:
  webdata:
  sshdata:
  tunneldata:

Git Push

Once the containers are running and the remote Git repository is accessible via the Cloudflare Tunnel, changes can be deployed using a standard Git push. From the local development machine:

git remote add origin ssh://[email protected]:/var/www/html
git push origin main

This immediately triggers the post-receive hook on the server, which checks out the current commit to the working directory. As a result, updated HTML files are reflected live on the website without further processing.

The repository is configured to ignore .tex source files using a .gitignore entry. This ensures that only the generated .html documents are deployed:

*.tex

This separation keeps the source and published formats distinct and avoids uploading raw LaTeX source to the production site.

Results

All pushes to the repository are instantly reflected on the website without a build step or deployment. The working tree is live, and changes are applied in real time.

Discussion

The system avoids all forms of templating engines, CMSs, or scripting beyond PHP for listing files. The site is plain HTML, generated from LaTeX and Pandoc, with version control via Git. Updates occur only when content is worth preserving.

References

Docker Inc. (2025). Docker: Empowering App Development for Developers. Retrieved from https://www.docker.com

MacFarlane, J. (2006–2025). Pandoc: A Universal Document Converter. Retrieved from https://pandoc.org

Merkel, D. (2014). Linux containers. Linux Journal, 2014(239).




Home | Privacy Policy