AlchemyPub #
AlchemyPub is a static site generator on fire built with Elixir & Phoenix LiveView.
It generates websites from markdown files. Changes to the source files are instantly published to all viewers. If no JavaScript is available on the client, it falls back gracefully to fully server-side rendered content. There is also an RSS feed generated from the articles.
Instead of saving generated pages as html files, they are rendered on startup and stored in memory using ETS. A file watcher picks up changes and broadcasts them using PubSub. Using the magic of Phoenix LiveView the change is immediately visible to all page viewers.
As a markdown parser, MDEx is used. It parses CommonMark with GFM tables, strikethrough and [[Wikilinks]] enabled, and its document AST is what the engine transforms: anchors are generated for headers, slides and columns are split at thematic breaks, and speaker notes are stripped from public decks.
For styling, daisyUI allows easy change of themes and creation of your own style using Tailwind. The site is fully responsive for mobile and desktop resolutions, and supports themes for dark and light mode. Code blocks are highlighted at compile time by Lumis, so no JavaScript runs in the browser for it.
With LiveDeck, AlchemyPub comes with an interactive presentation framework. It can turn any markdown page into a dynamic, interactive presentation. You can see an example of LiveDeck running here.

Pages can be turned into a onesheet — a dense, print-ready single-page reference layout — by setting onesheet: true in the frontmatter. Content is split into flex columns using --- dividers and scales to fit one A4 page when printed.
Setting forward: <url> in a page's frontmatter turns it into a redirect, sending visitors to an external URL. This is useful for creating short links or named redirects within the site.
Sites and their articles can be published to the AT Protocol network as standard.site records, kept in sync with the pages from the account configured in config/runtime.exs.
Further page options are documented on their own pages: an image gallery with lightbox and blur-up placeholders, password protected pages, embedding an external site via iframe, and pulling a Bluesky post with its replies below a page so the discussion happens on the AT Protocol network. Two layouts are available, a classic sidebar and a minimal top bar, selected with nav_style in config/config.exs.
Page visits are tracked anonymously. Thanks to Phoenix Presence, it can keep track of navigation and the duration of each page visit. This also powers the live online counter in the navigation bar. The tracking data — including page, duration, referrer, user agent, and country — is stored in a file-based SQLite database using Ecto. No external database or configuration is required. The tracking data can be explored through a custom Phoenix LiveDashboard analytics page.
Using AlchemyPub for your own site #
AlchemyPub runs standalone (this repository is the demo site), but the intended way to build a site is as a dependency: your repository holds only content, configuration and styling, and platform updates arrive with mix deps.update alchemy_pub. See examples/site for a complete, minimal site that does exactly that.
Getting Started #
To start your Phoenix server:
- Run
mix setupto install and setup dependencies - Start Phoenix endpoint with
mix phx.serveror inside IEx withiex -S mix phx.server
Now you can visit localhost:4000 from your browser.
Put your pages as markdown files in the priv/pages directory. You can read more on the Home page.
Ready to run in production? Please check the Phoenix deployment guides.
Release #
mix assets.deploy
MIX_ENV=prod mix release
mix phx.digest.clean --all
PHX_SERVER=true PHX_HOST=localhost SECRET_KEY_BASE="{{ key }}" _build/prod/rel/alchemy_pub/bin/alchemy_pub start
Content outside the release #
A release carries its own copy of priv, so pages and images change only with a new
release. Pointing CONTENT_DIR at a directory with pages and images, for example the
priv of the checked out repository, keeps them live across rebuilds: a git pull is
enough to publish new content, and the running site picks it up within seconds.
CONTENT_DIR=/srv/site/priv PHX_SERVER=true ... _build/prod/rel/alchemy_pub/bin/alchemy_pub start
Deployment on fly.io #
Set an admin secret
flyctl secrets set ADMIN_SECRET=(openssl rand -base64 48)
And deploy
flyctl deploy
Credits #
AlchemyPub is originally brought to you by j4nk.dev. It is published under the GNU General Public License v3.0.