Elk: a nimble Mastodon web client you can self-host behind your own proxy
A nimble Mastodon web client. The Docker container itself does not provide any SSL/TLS handling.
At a glance
- What is it?
- Elk is a Vue and Nuxt front end for Mastodon servers, shipped as an MIT-licensed repository with a Docker image that deliberately does no TLS. Here is what the README actually promises, and where the setup bites.
- Who is it for?
- Adopt Elk if you run a Mastodon-compatible instance and want a modern client you control, or if you want to log into any compatible instance through a deployment like elk.fedified.com. Do not adopt it if you expect the container to terminate TLS, or if you want a client with a documented rollback and upgrade path; the README describes neither.
- Can I use it commercially?
- Yes. MIT 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 28 days ago.
- What is it written in?
- Mainly Vue, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 27, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The problem Elk solves for Mastodon instance operators
Mastodon ships a web interface, and for a lot of people it is fine. Elk exists for the cases where it is not: an operator who wants a faster, more app-like reading experience for their users, or a person who wants one client that can log into any compatible instance rather than the one they happen to have an account on. The README frames the project plainly as "A nimble Mastodon web client", and the ecosystem list shows both patterns in use. Some deployments such as elk.h4.io or elk.mastodonapp.uk are tied to a specific server. Others, including elk.fedified.com, elk.me.uk and elk.bolha.us, are described as letting you log into any compatible instance.
The intended audience is narrower than a general social app user. This is a repository you clone, build and run. The README's deployment section is written for someone who already has a server, a domain and a reverse proxy. If you just want to read Mastodon on your phone, the hosted deployment at elk.zone is the path, and the README points there first. Self-hosting is for operators and for people who want to control the front end their users see.
One thing the README is explicit about, and it matters: community deployments listed in the ecosystem section are not maintained by the Elk team and may not be synced with Elk's source. That is a direct warning against treating the list as a vetted directory of public instances.
How Elk is put together: Nuxt, Vue and a server-side storage layer
The stack list in the README is concrete: Vite for tooling, Nuxt as the framework, Vue underneath, Pinia for state, UnoCSS for styling, Masto.js as the TypeScript Mastodon API client, and vite-plugin-pwa for update prompts, web push and the Web Share Target API. That combination tells you what kind of project this is. It is a server-rendered Nuxt application, not a static single-page bundle, and the Dockerfile confirms it: the final image copies /elk/.output and runs node .output/server/index.mjs.
The data flow follows from that. The browser talks to the Elk server, and Elk's server side talks to Mastodon instances through Masto.js. Because Elk holds account configuration server-side, it needs persistent storage, which is why the Dockerfile declares a volume at /elk/data and sets NUXT_STORAGE_FS_BASE to that path. The .env.example exposes the storage choice directly with NUXT_STORAGE_DRIVER, whose commented values are 'cloudflare', 'vercel' or 'fs'. A filesystem driver is the self-hosted default; the Cloudflare and Vercel options come with their own credential variables (NUXT_CLOUDFLARE_ACCOUNT_ID, NUXT_CLOUDFLARE_NAMESPACE_ID, NUXT_CLOUDFLARE_API_TOKEN) and are labelled production only.
The same file lists deployment-shaping switches: NUXT_PUBLIC_DEFAULT_SERVER to pin a default instance, NUXT_PUBLIC_SINGLE_INSTANCE to restrict Elk to one server, NUXT_PUBLIC_TRANSLATE_API, NUXT_PUBLIC_PRIVACY_POLICY_URL, NUXT_ADMIN_KEY, NUXT_PUBLIC_DISABLE_VERSION_CHECK, and a set of NUXT_GITHUB_* variables. That is a wider configuration surface than the short deployment section suggests, and the README does not walk through any of them.
Self-hosting Elk with Docker on port 5314
The README gives a five-step Docker path. Clone the repository, enter the directory, create a storage directory, change its ownership, then bring the container up. The ownership step is not optional, and the README explains why in a note: the container runs Elk as a non-root user with UID and GID 911, the persistent volume is created with root permissions, and if /elk/data is not writable by 911 the app cannot store config for user accounts.
git clone https://github.com/elk-zone/elk.git
cd elk
mkdir elk-storage
sudo chown 911:911 ./elk-storage
docker compose up --build -dThe compose file is short. It builds from the repository's Dockerfile, mounts ./elk-storage at /elk/data, and publishes port 5314.
services:
elk:
build:
context: .
dockerfile: Dockerfile
volumes:
- './elk-storage:/elk/data'
ports:
- 5314:5314Once it is up, the container listens on 5314 and the README's warning applies immediately: Elk only loads properly if the connection is made over SSL/TLS, and the container provides no TLS handling itself. The README suggests putting Elk behind a reverse proxy with SSL, naming Traefik and NGINX. If you prefer to run from source rather than Docker, the contributing section gives the alternative: pnpm i then pnpm run dev, with corepack enabled. The dev script pins port 5314 as well, and a mocked variant exists via pnpm run dev:mocked.
pnpm i
pnpm run devThe permission trap and other rough edges
The most likely first failure is the one the README spends the most words on. Start the container without fixing ownership and Elk comes up but cannot persist account configuration. The README offers two fixes: correct the permission inside the created named volume, or mount a directory that already has the right permission to /elk/data. The compose file repeats the warning in a comment, which suggests it is a recurring support issue rather than a rare edge case.
The second constraint is TLS. The README states it twice in effect: the container does no SSL/TLS handling, and Elk only loads properly over SSL/TLS. That means the Docker path is not a complete deployment. You are expected to supply the proxy layer, and nothing in the repository documentation describes a certificate setup for production. There is an https-dev-config directory and a start:https script for local development, but the README does not present those as a production story.
Third, the README does not document upgrades or rollback. There is a release script (bumpp plus scripts/release.ts) aimed at maintainers, and the canary deployment at main.elk.zone is described as deploying on every commit to main, but neither tells an operator how to move a self-hosted instance from one version to the next or how to revert. If you self-host, that is a gap you have to plan around yourself.
Finally, the ecosystem list is a liability if read carelessly. The README states plainly that community deployments are not maintained by the Elk team and may not be synced with Elk's source, and asks readers to research host servers before using them.
Elk against the stock Mastodon web client and against a soft fork
The obvious alternative is the web interface that ships with Mastodon itself. The difference is architectural rather than cosmetic. Mastodon's client is served by the instance you are logged into and is bound to that instance's version. Elk is a separate Nuxt application that you deploy independently and that talks to instances through the Masto.js API client. That separation is what allows one Elk deployment to serve users from many servers, and it is also what creates the storage and TLS obligations the Mastodon-served client does not have. Choosing Elk means taking on a second service to run.
The repository also points at a fork rather than a competitor. The ecosystem list includes crab, described as a soft fork of Elk, used for the bumscode.com server. A soft fork keeps the upstream shape and diverges in places, which is a different trade-off from configuring Elk through environment variables: you get changes the upstream project has not accepted, and you inherit the work of tracking upstream. The README does not compare the two, and it does not explain what crab changes.
For anyone deciding between Elk and the built-in client, the deciding question is who operates the front end. If the answer is "the instance, using what Mastodon ships", Elk adds nothing. If the answer is "us, with our own domain and proxy", Elk is the option this repository describes.
Licence, maintenance and what an upgrade actually costs
Elk is MIT licensed, copyright 2022 to present Elk contributors, per the LICENSE file and the package metadata. MIT is permissive: you can run, modify and redistribute it, including in a commercial deployment, provided the licence and copyright notice are preserved. That is a summary of the licence text, not legal advice, and if you are deploying Elk as part of a paid service you should read the LICENSE file and take your own advice.
On maintenance, the facts are the dates. The last push to main was on 2026-07-30, and the most recent release, v1.0.1, carries the same timestamp. Before that, v1.0.0 landed on 2026-06-05 and v0.17.3 on 2025-12-03. The repository is not archived. The gap between v0.17.3 in December and v1.0.0 in June is worth noting if you depend on frequent releases; the project does not appear to ship on a fixed cadence, and the README's canary deployment on every commit to main is the closest thing to a continuous channel.
Upgrade cost is where the documentation is thinnest. Because the Dockerfile builds from source with pnpm and a frozen lockfile, rebuilding is the update mechanism the README implies, but it does not describe a migration step for /elk/data or a way to pin and revert a version. Operators who need predictable rollback should treat that as something to solve outside the project's own instructions.
Editorial conclusion
Adopt Elk if you run a Mastodon-compatible instance and want a modern client you control, or if you want to log into any compatible instance through a deployment like elk.fedified.com. Do not adopt it if you expect the container to terminate TLS, or if you want a client with a documented rollback and upgrade path; the README describes neither. Before you commit, verify two things: that /elk/data is writable by UID 911, and that a reverse proxy in front of port 5314 is actually serving valid certificates, because the README states Elk only loads properly over SSL/TLS.
Frequently asked questions
Does the Elk Docker container handle SSL/TLS?
No. The README states that the Docker container itself does not provide any SSL/TLS handling and that Elk only loads properly when the connection is made over SSL/TLS, so you have to add that layer yourself, for example with a reverse proxy such as Traefik or NGINX.
Why can't Elk store config for user accounts after I start the container?
The persistent volume is created with root permissions while Elk runs as UID:GID 911, so /elk/data is not writable. The README says to either fix the permission inside the created named volume or mount a directory that already has the correct permission to /elk/data.
Which port does Elk listen on?
The Dockerfile sets PORT=5314 and exposes 5314/tcp, the compose file publishes 5314:5314, and the dev script also runs Nuxt on port 5314.
Can I use Elk with any Mastodon instance?
The ecosystem list includes deployments such as elk.fedified.com and elk.me.uk that are described as letting you log into any compatible instance, while others are tied to a single server such as elk.h4.io for h4.io. The README also notes that community deployments are not maintained by the Elk team.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/elk-zone-elk)