Self-hosted service
CoreBunch/Instatic avatar
CoreBunch/Instatic

Instatic: A Self-Hosted Visual CMS That Ships Plain HTML

The open-source alternative to Webflow, Framer and WordPress. Agentic self-hosted visual CMS outputting clean static pages. Users, roles, plugins, content, database, it's all there.

8,819 stars828 forksTypeScriptMIT

At a glance

What is it?
Instatic is an MIT-licensed, Bun-based CMS that combines a visual canvas editor, content engine and publisher in one server, and outputs static pages without the editor's runtime. Here is what the repository documents, and where the trade-offs sit.
Who is it for?
Adopt Instatic if you want to own the whole pipeline, are comfortable with Bun and Docker, and care that the published HTML stays free of builder markup. Do not adopt it if you need a stable, documented API surface today or if your team cannot run a server: the release line is at 0.0.20 and the README does not document rollback.
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 17 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 29, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What Instatic replaces, and who it is aimed at

A typical marketing site is assembled from a headless CMS, a front-end framework, a host, a form service, an analytics vendor and an image CDN. Each of those is a separate bill, a separate dashboard and a separate thing that can go down at 2 a.m. Instatic's README frames the project as the opposite bet: one Bun server holding the canvas editor, the content engine, media, auth, forms, plugins and the publisher, backed by SQLite or Postgres.

The target user is someone who wants a visual page builder but does not want the builder to follow the page into production. The README is explicit that the output is plain semantic HTML and compact CSS, with none of the editor's machinery left in the page: no framework runtime, no builder attributes, no div soup. That is the pitch. It is aimed at self-hosters, small agencies and developers who would otherwise reach for Webflow, Framer or WordPress and then spend time fighting the generated markup.

The project is written in TypeScript, licensed MIT, and the homepage points at instatic.com. The README positions it as an open-source alternative to those three products rather than a drop-in replacement, which is the honest framing: the feature set overlaps, the operating model does not.

How the Bun server, the canvas and the publisher fit together

The repository layout tells most of the story. There is a server/ directory, an src/ directory, and a build split across TypeScript compilation and a Vite step: the build script is tsc -b followed by bun run scripts/vite.ts build. The Dockerfile carries this through in three stages, a build stage, a production-deps stage and a runtime stage, with the runtime setting PORT=3001, STATIC_DIR=/app/dist and UPLOADS_DIR=/app/uploads.

The editing model is a real canvas. The README says you can place several breakpoint frames side by side and edit them together, so a change to the desktop frame is reflected in the mobile frame in the same view, and there is a live mode for editing a single full-size page in place.

The design-token layer is Core Framework, which the README describes as built in rather than installed as a plugin. It generates tint and shade scales from a single brand color, produces a fluid type ramp instead of hand-picked font sizes, and emits utility classes into one framework.css. The README's claim is that the design system lives as data, so changing one token updates every page that uses it.

On the content side, modules are the building blocks (containers, text, images, buttons, video, lists, links, SVG, forms), Visual Components are reusable pieces with typed parameters and named slots, and Templates handle shared chrome plus a designable 404 page. The README notes that components which would reference themselves are blocked before they happen. Build output lands in dist, which is what the runtime serves as STATIC_DIR.

Installing Instatic: Railway template or a single Docker image

The README offers two paths. Railway is described as the fastest: pick a template, hit the button, and it generates the secret keys, attaches the storage volume and sets up health checks. The template table lists a SQLite option for a single site and a Postgres option for multiple authors and managed backups. Render is offered as a guide for teams that prefer Render services, and there is a VPS guide for bring-your-own-server with Caddy TLS.

If you prefer your own hardware, the README gives a one-line Docker invocation. The image is pulled from the GitHub Container Registry and composed with the production and SQLite compose files:

bash
INSTATIC_IMAGE=ghcr.io/corebunch/instatic:latest docker compose -f compose.prod.yml -f compose.sqlite.yml up -d

Running that brings up the stack with the container listening on the port the image sets. The README says updating works the same way: redeploy the latest image, and the database and uploads stay on the attached storage.

For local development the .env.example is clear that you usually need no configuration at all. The default is SQLite at .tmp/dev.db with no Docker involved, and the dev script starts a Postgres service automatically only when DATABASE_URL points at Postgres. The example file documents the three database forms:

bash
DATABASE_URL=sqlite:./path/to/your.db
DATABASE_URL=sqlite::memory:
DATABASE_URL=postgres://instatic:[email protected]:5433/instatic

One production requirement is spelled out in the same file: local dev auto-creates .tmp/secret.key, but production deployments must set INSTATIC_SECRET_KEY to the output of a generator script.

bash
bun run scripts/generate-secret-key.ts

The package manifest pins the runtime tightly: engines declares bun >=1.4.0 <1.5.0, and packageManager is [email protected]. The Dockerfile reads the same version from an ARG, and a comment notes that CI reads it from package.json instead. If you build the image yourself, that version pairing is the first thing to check.

Where Instatic is the wrong tool

The most visible constraint is maturity. The current release is v0.0.20, published 2026-09-13, and the two prior releases, v0.0.19 and v0.0.18, landed on 2026-09-10 and 2026-09-02. That is a rapid cadence on a 0.0.x line, which is normal for a young project and also means the surface is still moving. Anyone who needs a frozen API, a long deprecation window or a stable plugin contract should treat that as a real risk rather than a footnote.

The README has an "Early on purpose" section referenced in the navigation, and the honest reading is that the project knows what it is. What the README does not document is rollback. It says updating is a redeploy and that the database and uploads persist on attached storage, but there is no documented procedure for reverting a schema change if a new image expects a different database shape. That is the gap to probe before running this against a site you care about.

There is also a runtime bet. Instatic requires Bun, pinned to a minor range, and the entire server runs on it. If your organisation standardises on Node, or your hosting platform cannot run a Bun image, that is a hard stop rather than a configuration problem.

Finally, this is a CMS, not a general application framework. If your site needs authenticated user sessions, server-side personalisation or a database-backed API at request time, the static-output model works against you. The README's own framing, that the site loads like a static file because most of the time it is one, is the boundary.

Instatic compared with WordPress, Webflow and Framer

The README names all three, so the comparison is fair game. WordPress is the closest in operating model: self-hosted, PHP, a plugin ecosystem, and a database behind every page load. Instatic differs in two ways. It runs on Bun and TypeScript rather than PHP, and the published artefact is static rather than rendered per request. The README's Core Framework note is pointed here: it describes Core Framework as something thousands of WordPress professionals already use, and says that in Instatic it is a core system rather than a plugin. That is a direct claim about the WordPress plugin model, where a design-token engine is one dependency among many.

Webflow and Framer are hosted visual builders. The difference is not the editor, it is who holds the infrastructure. With either, you publish to their hosting and accept their pricing and their export limits. With Instatic you run the server, own the database, and the output is yours to serve from anywhere. The cost is that you also own uptime, backups and upgrades.

The plugin system is the other axis. The repository ships examples/plugins/ and a docs/features/plugin-system.md, and the README lists plugins alongside auth and forms as part of the single server. That is a different shape from a hosted builder's app marketplace: plugins here are code you run, which is more flexible and more your problem when one breaks.

Maintenance, upgrades and what the MIT licence actually covers

The last push to the default branch was on 2026-09-13, and the most recent release, v0.0.20, was tagged the same day. The repository is not archived. On the evidence of the release history, this is a project that ships often, and the upgrade path the README documents is redeploy rather than migrate: pull the latest image, keep the database and uploads on attached storage, and the container is replaced. That is a low-ceremony upgrade for a patch release and a genuine question mark for anything that changes the schema.

Operationally, the things you own are the database (SQLite file or Postgres), the uploads directory, and the secret key. The .env.example is explicit that production must set INSTATIC_SECRET_KEY, generated by a script in the repository, and the Dockerfile sets UPLOADS_DIR and STATIC_DIR as environment variables. A backup policy that covers only the database and not the uploads volume will lose media.

The licence is MIT, declared in package.json and in the LICENSE file, and the Dockerfile carries an org.opencontainers.image.licenses label of MIT. MIT is permissive: it allows commercial use, modification and redistribution with the copyright notice and permission notice retained. It provides no warranty, which is worth stating plainly for a 0.0.x project. That is a description of the licence text, not legal advice; if you are redistributing Instatic inside a product, have your own counsel read the LICENSE file rather than this paragraph.

One more cost to budget: the repository contains a full development toolchain, including Playwright end-to-end tests, ESLint, a coverage pipeline and a bundle-size budget test. If you fork it, you inherit that surface. If you only deploy the image, you do not.

Editorial conclusion

Adopt Instatic if you want to own the whole pipeline, are comfortable with Bun and Docker, and care that the published HTML stays free of builder markup. Do not adopt it if you need a stable, documented API surface today or if your team cannot run a server: the release line is at 0.0.20 and the README does not document rollback. Before committing, verify the current release version, read docs/deployment/vps.md, and confirm that INSTATIC_SECRET_KEY generation and your backup policy cover the uploads directory as well as the database.

Frequently asked questions

What is Instatic?

Instatic is a self-hosted CMS that puts a visual canvas editor, content engine, media, auth, forms, plugins and a publisher in one Bun server, backed by SQLite or Postgres. It is MIT licensed and outputs plain semantic HTML and compact CSS without the editor's runtime in the page.

How do I install Instatic on my own server?

The README gives a single Docker image and a compose command using compose.prod.yml and compose.sqlite.yml, with the image pulled from ghcr.io/corebunch/instatic. Full guides for VPS, Postgres, HTTPS with Caddy, Render and backups are in docs/deployment.

Does Instatic need Bun, or can I run it on Node?

The package manifest declares bun >=1.4.0 <1.5.0 in engines and sets packageManager to [email protected], and the Dockerfile builds on oven/bun images. No Node runtime path is described.

Can I use Postgres instead of SQLite with Instatic?

Yes. The .env.example documents a Postgres DATABASE_URL, and the README's deploy table lists a Railway Postgres template for multiple authors and managed backups. It recommends SQLite as the default for most sites and Postgres when you have a team of authors or want managed database backups.

What do I need to set before running Instatic in production?

The .env.example states that production deployments must set INSTATIC_SECRET_KEY to the output of bun run scripts/generate-secret-key.ts, since local dev only auto-creates .tmp/secret.key. The Dockerfile also sets PORT, STATIC_DIR and UPLOADS_DIR.

Official sources

  1. CoreBunch/Instatic on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
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.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/corebunch-instatic.svg)](https://hysenlabs.com/projects/corebunch-instatic)