Clone this repo, run ./bootstrap and docker compose up, and you have an
IndieWeb site running the
Indiekit reference Eleventy theme
with Indiekit behind it, over HTTPS, on any
Docker host.
-
Clone with the theme submodule:
git clone --recurse-submodules https://github.com/getindiekit/indiekit-quickstart cd indiekit-quickstart -
Answer a few questions, then start:
./bootstrap docker compose up -d
bootstrapasks for your site URL, a password for the admin interface, and who you are. It writes.env(filling in.env.example, generating the signing secret and hashing your password) andsite.json(filling insite.example.jsonwith your answers). It never overwrites either without--force.Open
SITE_URLfor your site, andSITE_URL/poststo write. To set it up without a terminal, setINDIEKIT_PASSWORD(and optionallyINDIEKIT_SITE_URLandINDIEKIT_AUTHOR_NAME) and run./bootstrapfrom a script. PassingINDIEKIT_PASSWORDinline on the command line puts it in your shell history — export it in the script instead.Until
ghcr.io/getindiekit/indiekit:betais published (see below),./bootstrapneeds to know to use the locally-built image too:COMPOSE_FILE=compose.yml:compose.local.yml ./bootstrap COMPOSE_FILE=compose.yml:compose.local.yml docker compose up -d
ghcr.io/getindiekit/indiekit:beta is what compose.yml expects. Until it's
published — or to test a local build of it — build the image yourself and
point the stack at it with the local override:
docker build -f /path/to/getindiekit/indiekit/docker/Dockerfile -t indiekit:local /path/to/getindiekit/indiekit
docker compose -f compose.yml -f compose.local.yml up -dcompose.local.yml swaps the indiekit service's image for indiekit:local
and changes nothing else.
Indiekit speaks Micropub, so any
Micropub client works, not just the built-in editor. Sign in to a client with
your SITE_URL and it will discover the endpoints on its own.
git pull --recurse-submodules
docker compose up -d --buildcontent/is yours: one directory per post type (articles,notes,photos,bookmarks,likes,replies,reposts,rsvps,events,jams,audio,videos),pagesfor/about-style pages, andmediafor uploads. Indiekit writes there; the site is built from there. Back it up.site/is the theme (a git submodule). Customise your site throughsite.jsonhere, or through the theme's own_data/site.jsonkeys — see the theme README for the full list.- The built site itself lives in a Docker volume, not on disk — it's
regenerated from
content/and the theme on every start and after every change.
To run this on your own machine with no domain and no certificate, accept
bootstrap's default site URL — http://quickstart.localhost — by pressing
Enter at the first question. It derives SITE_HOST from whatever you answer,
so the two can never disagree.
Any name ending in .localhost resolves to your machine in browsers and on
most systems, and unlike plain localhost it can also be reached from inside
the Indiekit container, which sign-in needs.
If port 80 or 443 is already taken, set HTTP_PORT / HTTPS_PORT in .env
after bootstrapping, and give bootstrap a site URL carrying the same port
(e.g. http://quickstart.localhost:8088 with HTTP_PORT=8088).
To see the theme with content before you have any, set DEMO=1 in .env
(with the local try-out values above) and start the stack. The theme's
sample posts are copied into content/, the site builds with the theme's
demo identity and sample webmentions, and site.json is left unread. Posting
still works: sign in at SITE_URL/posts and your note appears next to the
samples.
Back to your own site: set DEMO=0, delete content/*/fixture-*, and
restart the site service. With DEMO=0 the samples are hidden even if
the files are still there.
COMPOSE_FILE=compose.yml:compose.local.yml node test/kit.mjsThere is no package.json and nothing to install: the checks use Node's
built-ins only, so Docker stays the single prerequisite.
COMPOSE_FILE is needed until ghcr.io/getindiekit/indiekit:beta is
published. Two checks start a real container — one hashes a password and
verifies the hash, the other runs bootstrap in a fresh clone — so build
indiekit:local first, as "Before the image is published" describes. Without
it those checks fail on the image, not on your change.
The suite is idempotent: run it twice in a row and git status should be
clean both times. If it is not, something wrote into the working tree that
should have used a temporary directory.
bootstrap is the command; bootstrap.lib.mjs holds the parts with no
input, output or Docker in them — generating the secret, deriving the host
from the URL, rendering .env and site.json. That split is what lets the
checks assert them directly, without a terminal or a container. Logic worth
testing belongs in the library; prompts, files and subprocesses belong in the
command.
Every prompt goes through the ask and askList helpers. They own two things
that are easy to get wrong separately: falling back to the default when there
is no terminal, and turning Ctrl-D into a clean exit rather than a stack
trace. Add a prompt by calling them — a bare rl.question reintroduces both
bugs.
The pristine copy of site.json, and the baseline bootstrap compares
against to decide whether you have customised your identity. It is not a
file to edit. If it drifts from the shipped site.json, every fresh clone
looks "already edited", bootstrap declines to write identity, and the site
publishes with no h-card — silently. Change site.json and site.example.json
together, or not at all.
All of native Indiekit that works with a file store and the Eleventy preset:
every post type (article, note, photo, bookmark, like, reply, repost, RSVP,
event, jam, audio, video, page), every default endpoint, plus Microsub and
webmention.io. The three syndicators (Bluesky, Mastodon, Internet Archive)
switch on when their credentials are set in .env; see .env.example.
INDIEKIT_LOCALE=en there pins the admin interface's language; empty, it
follows your browser.
Indiekit's page post type is for /about, /now, /uses: create one
from the editor and it is served at /<slug>/. Creating a page does not put
it in the menu, and a page can stay out of the menu on purpose. To list
pages in the rail, name their paths in site.json, in the order you want:
"navigation": ["/about/", "/now/"]The site rebuilds on its own after you save. A path that is not a page you have created fails that build with a message naming it, so the menu can never hold a dead link; the build log also names any page that exists but is not listed.
Every page you have created is listed at SITE_URL/pages/, in the menu or
not, and the Collections page links there with the count.
The stack runs four images. Two are stock, two are Indiekit's:
| Image | Size |
|---|---|
ghcr.io/getindiekit/indiekit:beta |
718 MB when built locally from the pull request that adds it; not yet published |
site (built from site.Dockerfile) |
488 MB, of which the node:24-alpine base is 234MB and the theme's dependencies 175 MB |
mongo:8 |
stock image |
caddy:2-alpine |
stock image |