xyproto/algernon: a Go web server that renders Lua, Markdown and React 19 without npm
Small self-contained pure-Go web server with Lua, Teal, Markdown, HTTP/2, QUIC, Redis, TypeScript, npm-less React 19, SQLite, and PostgreSQL support ++
At a glance
- What is it?
- Algernon is a single self-contained Go executable that serves HTTP/2 and HTTP/3 and turns directories of .lua, .md, .jsx and .tsx files into a running site. Its file-convention routing is the whole point, and the whole constraint.
- Who is it for?
- Adopt Algernon if you want a small Go binary that turns a directory of Markdown, Lua and JSX into a running site without a build toolchain, and if filename-based routing matches how you already think about your content. Do not adopt it if you need a documented rollback path, a configuration format that a tool can generate, or long-term support guarantees, because the README does not describe any of those.
- Can I use it commercially?
- Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 4 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The problem Algernon solves: a directory that is already a website
Most small sites need a runtime, a bundler and a process manager before the first page renders. Algernon collapses that into one executable. The README describes it as a web server with built-in support for HTTP/2, HTTP/3 (QUIC), Lua, Teal, Markdown, Pongo2, Amber, Sass, GCSS, JSX, TypeScript, Ollama, BoltDB, Redis, PostgreSQL, SQLite, MariaDB, MySQL and MSSQL, all in one small self-contained executable. The audience is the person who wants to write a Markdown file, drop a Lua handler next to it, and have the server figure out the rest.
The design decision that carries the most weight is the filename convention. The README lists a prioritized order for special filenames in a directory: index.lua, index.html, index.md, index.txt, index.pongo2 (or .po2/.tmpl), index.amber, index.hyper.js, index.jsx, index.tsx, index.tl and index.prompt. Whichever one is present becomes the handler for that directory. There is no routes file and no configuration needed to get started, which the README states explicitly. That is a real reduction in setup cost, and it is also the thing you will fight with the first time two conventions collide in one folder.
The project also ships a second entry point: if a single Lua script is passed as a command line argument, it is used as a standalone server for setting up handlers or serving files and directories under specific URL prefixes. So the same binary covers both the convention-driven mode and a scripted mode.
How the file conventions and the Lua layer actually fit together
The mechanism is a lookup, not a router. A request maps to a directory, and the directory's index file decides the response. If the index is index.md, the README says the Markdown is rendered as HTML. If it is index.jsx or index.tsx, it is a React file rendered as HTML with bundled JavaScript, and esbuild does the bundling and JSX/TSX conversion. If it is index.lua, the script is interpreted as a handler function for the current directory. Teal, via index.tl, is the same idea with type safety.
The interesting part is data.lua. The README states that it is Lua code whose functions and variables are made available for Pongo2, Amber and Markdown pages in the same directory. That gives you a per-directory data layer without a database round trip in the template itself. style.gcss works the same way for styling: it is GCSS code used as the style for all Pongo2, Amber and Markdown pages in the same directory. Both are directory-scoped, which is a clean mental model and a sharp edge if you expected them to cascade to subdirectories.
Permissions are also convention-driven. The README states that /data and /repos have user permissions, /admin has admin permissions and / is public by default, and that this is configurable. Users and permissions come from the permissions2 library. Rate limiting is handled by tollbooth, logging by logrus, and plugins by pie. HTTP/2 over TLS is used by default when a certificate and key are supplied; otherwise the server falls back to regular HTTP. HTTP/3 over QUIC is opt-in behind a flag. The .prompt extension is the outlier: an index.prompt file contains a content type, an Ollama model, a blank line and a prompt, and the page is generated by an LLM.
Installing Algernon and serving a first Markdown page
The README states that Go 1.26 or later is required, and gives the install as a single go install command. The repository also ships a vendored dependency tree, so the manual path builds with -mod=vendor.
go install github.com/xyproto/algernon@latestIf you prefer to build the development version, the README gives this sequence. The welcome.sh script is part of the repository and the Makefile also references it for documentation installs.
git clone https://github.com/xyproto/algernon
cd algernon
go build -mod=vendor
./welcome.shThe Docker path is the fastest way to see the convention in action. The README gives this exact example, which creates a directory named localhost, writes an index.md into it, and mounts the current directory at /srv/algernon inside the container. The image is described as less than 17MB.
mkdir localhost
echo 'hi!' > localhost/index.md
docker run -it -p4000:4000 -v .:/srv/algernon xyproto/algernonAfter that, the README says to visit http://localhost:4000 in a browser, where the rendered Markdown should appear. Note the port: 4000, both in the -p flag and in the URL. The README does not document changing it in this example. For a first real use beyond a static page, the repository's samples/ tree contains worked directories including samples/lua/, samples/luacounter/, samples/jsx/, samples/hyperapp/, samples/htmx/ and samples/local_llm/, and TUTORIAL.md is the document the README points to for getting started.
Where the filename convention becomes a liability
The prioritized index list is a genuine constraint, not a convenience. If a directory contains both index.md and index.lua, the README's ordering decides which one wins, and the other file is silently not the handler. There is no error for the shadowed file. Teams that generate files, or that copy an index.md into a directory that already has an index.lua, will see the wrong page and no diagnostic pointing at the cause.
The second limitation is scope. data.lua and style.gcss apply to pages in the same directory. The README does not describe inheritance into subdirectories, so a site with a shared header will either duplicate the Lua file per directory or use the standalone-script mode instead. That is a design trade-off, and for a small site it is fine; for a site with a deep tree it is friction.
The third is operational. The README documents graceful shutdown as a feature and lists configuration by command line flags or a Lua script, but it does not document rollback, a migration path between versions, or what changes between v1.17.9, v1.17.10 and v1.17.11 beyond the version numbers. The ChangeLog.md file exists at the repository root, so that is where to look, but the README itself is silent. If you need a documented upgrade procedure before you deploy, Algernon does not give you one in its main document.
Finally, the .prompt extension depends on Ollama and a model name embedded in the file. That means page content depends on a local model being reachable and on the model behaving the same way over time. The README describes the file format but not what happens when Ollama is unavailable.
Algernon compared with a conventional Go web framework
The honest alternative is not another all-in-one server; it is the ordinary Go stack, a router such as net/http with a mux plus a template package, or a framework in that family. The difference in approach is where routing lives. In a conventional Go server you write handler registrations in code or a config file, and the mapping from URL to function is explicit and greppable. In Algernon the mapping is the filesystem, and the URL is the path. That means adding a page is creating a file, and it also means the routing table is only visible by listing directories.
A second real alternative for the same job is a static site generator plus a plain file server. It gives you a build step, a preview, and artifacts you can diff. Algernon gives you no build step for Markdown and templates, and it does bundle JSX and TypeScript at request time through esbuild, but it does not produce a deployable static output that the README describes. If your goal is a CDN-hosted static site, a generator is the better fit and Algernon is the wrong tool.
The comparison that matters most is the dependency footprint. Algernon vendors its dependencies and ships as one executable with a built-in Bolt database option, so there is no separate database process required for a small deployment. A conventional Go service plus PostgreSQL is three moving parts before the first request. That is the trade Algernon is making, and it is a reasonable one for a single-operator site.
Licence, maintenance and what an upgrade actually costs
Algernon is BSD-3-Clause, per the repository metadata and the LICENSE badge in the README. That is a permissive licence, and it means you can ship the binary inside a product. It does not mean the bundled dependencies share that licence: the go.mod file lists libraries including quic-go, esbuild, pongo2, gopher-lua, tollbooth, logrus and the permission backends, each with its own terms. The README does not enumerate their licences, and the FOSSA badge in the README points at a scan, so that is the place to check before redistribution. This is a description of what the repository states, not legal advice.
The last push to the default branch was on 2026-09-11, and the most recent release listed is v1.17.11 from 2026-07-26, preceded by v1.17.10 on 2026-07-04 and v1.17.9 on 2026-05-29. Those dates suggest a steady release cadence, and the repository is not archived. What the repository does not show is a support policy, a deprecation window, or a statement about which Go versions will keep working. The README pins the requirement at Go 1.26 or later, and go.mod declares go 1.26.0, so a toolchain older than that will not build it.
The upgrade cost is therefore mostly the toolchain and the vendored tree. Because the project builds with -mod=vendor, a dependency bump arrives with the source rather than through your own module graph. That removes version-conflict work and removes your ability to patch a transitive dependency without forking. The Makefile's default build flags include -trimpath and stripped symbols, which produces a smaller binary and a less informative stack trace. If you are debugging in production, build without those flags.
Editorial conclusion
Adopt Algernon if you want a small Go binary that turns a directory of Markdown, Lua and JSX into a running site without a build toolchain, and if filename-based routing matches how you already think about your content. Do not adopt it if you need a documented rollback path, a configuration format that a tool can generate, or long-term support guarantees, because the README does not describe any of those. Before committing, verify two things on your own machine: that Go 1.26 or later is available, and that the special filenames you plan to rely on (index.lua, index.md, index.tsx, data.lua, style.gcss) behave the way the README says when they sit in the same directory. Read TUTORIAL.md and samples/ first; they are the only worked examples the repository ships.
Frequently asked questions
What is xyproto/algernon?
It is a web server written in Go that ships as one self-contained executable, with built-in support for HTTP/2, HTTP/3 (QUIC), Lua, Teal, Markdown, Pongo2, Amber, Sass, GCSS, JSX, TypeScript, Ollama, BoltDB, Redis, PostgreSQL, SQLite, MariaDB, MySQL and MSSQL. Pages are selected by special filenames such as index.md, index.lua and index.tsx rather than by a routes file.
What Go version does Algernon require?
The README states that Go 1.26 or later is required, and go.mod declares go 1.26.0. The quick install is go install github.com/xyproto/algernon@latest.
Which index file wins when a directory has more than one?
The README gives a prioritized order: index.lua, index.html, index.md, index.txt, index.pongo2 (or .po2/.tmpl), index.amber, index.hyper.js, index.jsx, index.tsx, index.tl and index.prompt. The highest-priority file present becomes the handler for that directory.
Community notes