Open-source project
usertour/usertour avatar
usertour/usertour

Usertour: an open source onboarding platform that ships its own MCP server

Usertour is an open-source user onboarding platform. It allows you to create in-app product tours, checklists, and surveys in minutes—effortlessly and with full control.The open-source alternative to Userflow and Appcues

2,312 stars160 forksTypeScriptNOASSERTION

At a glance

What is it?
Product tours, checklists and surveys you self host with Docker. The interesting parts are the licensing split, the environment-aware webhook model, and an MCP endpoint that lets an assistant author the flows.
Who is it for?
Usertour is a credible self-hosted answer to the commercial onboarding tools it names in its own description, and the deciding factor for most evaluators will be licensing rather than features. The package manifest says MIT while GitHub cannot classify the repository and the tree carries a second licence file for enterprise terms, so read both before you build a commercial product on it.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 9 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 28, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What the project is and who it is aimed at

Usertour is an open source user onboarding platform. In the project's own words it lets you create in-app product tours, checklists and surveys, and the repository description positions it as an alternative to a specific list of commercial tools: Appcues, Userpilot, Userflow, Userguiding and Chameleon. Those names also appear as repository topics, alongside more descriptive ones like onboarding, checklists, surveys, tooltips, announcements, in-app, NPS, tour and plugin systems.

Naming competitors in the description is unusual for a commercial product and reasonable for an open source one, because the pitch is that you can read the code and run it yourself rather than trusting a vendor.

The feature set is the standard shape for this category. Flows and product tours, checklists, launchers and surveys. Targeting by custom user attributes and tracked events, so a flow appears only for the users who need it. Multi-environment support, meaning Production and Staging managed inside one account. Version tracking, so you can see who changed a flow and when. The README notes that flows work across single-page applications and multi-page apps alike, and that the integration works anywhere a browser runs, since the widget is injected client side.

The homepage is usertour.io and documentation is at docs.usertour.io. The README also links a blog, an X account and a Discord server.

Self hosting is two commands and three containers

The self-hosting path is documented in the README rather than hidden in a wiki, and it is genuinely short:

bash
cp .env.example .env # make sure all required envs are properly set
docker compose up -d

You then open http://localhost:8011. There is also a one-click Railway deployment button, and local development is pointed at CONTRIBUTING.md.

The compose file is the whole architecture, and it is worth reading because it tells you what state the system holds. One service builds from the repository Dockerfile and maps port 8011 to 80 on the host. Two more services are postgres:15-alpine and redis:alpine. Both have volumes, so their data survives a container rebuild, and both have health checks, with the app service declaring a dependency on each one being healthy rather than merely started.

That ordering detail matters more than it looks. A migration run against a Postgres that is up but not yet accepting connections is a classic self-hosting failure, and using condition: service_healthy with a pg_isready probe is the standard fix.

The Dockerfile is a two-stage build on node:22.13-alpine with pnpm 9.6.0 installed globally. The builder stage installs OpenSSL for Prisma, then does something worth noting: it generates a config.js file for the web app by scanning apps/web/.env.example for variables prefixed with VITE_ and emitting them into a browser global. That is how runtime configuration reaches a statically built front end, which otherwise would have needed rebuilding per environment.

The build runs four separate steps, server, web, sdk and an IIFE build of the sdk, then copies the sdk dist output into a known location using the version field read from the sdk package.json. The production stage installs nginx, OpenSSL, gettext and a postgresql16-client binary, and serves the web and sdk builds as static files from the nginx html directory. So the shipped container is nginx plus the Node server plus the compiled assets, which is a sensible split for a self-hosted product.

The production stage also carries a comment about preserving the workspace structure when copying node_modules, which is the usual pnpm requirement in a monorepo: the symlink farm has to come across intact.

Configuration tells you what the product actually does

The .env.example file is more informative about the product than the feature list, because it names every external system the application talks to.

Storage is Postgres with a primary and a direct URL. Email is configured with host, port, user and password. Redis has host, port, pass, user and a TLS flag, with the note that the host is simply redis inside compose. Object storage is AWS S3 with region, endpoint, credentials, bucket and domain, meaning file uploads for avatars or similar do not live in Postgres.

Authentication is the most detailed section, and it shows how many identity paths the project supports. Email authentication is enabled by default with a sender you configure. GitHub and Google authentication are present but disabled by default, each with a client id and secret and an optional callback URL that defaults to a path under /api/auth. There is also SSO over OIDC with its own callback override.

Each callback URL carries a comment explaining that it is optional and what the default is. That is the kind of detail that reflects a project where self-hosters hit these variables and asked for documentation.

One setting deserves attention from anyone deploying behind a corporate network. ALLOW_PRIVATE_NETWORK_EGRESS defaults to false, with a comment saying to set it true only if your SSO identity provider is reachable on an internal network. The variable name and the default together describe a defence against server side request forgery in the OAuth callback path, which is a real vulnerability class in self-hosted applications that fetch identity provider metadata by URL.

The rest is billing and session configuration. JWT_SECRET ships as the literal value test, so change it. Tokens expire after an hour and refresh tokens after seven days. Stripe keys, account webhook secrets and portal return URLs are present, which means self-hosters can charge their own users if they want to. There is a cookie domain and secure flag for the widget. LOGIN_REDIRECT_URL defaults to the flows page for the first environment. NODE_ENV is set to production, and a final variable holds the instance's public base URL, which the comment says OAuth and MCP methods fall back to when it is unset.

Environments and webhooks are the features to look at first

Two capabilities described in the release notes separate this from a simple tour builder, and both come from treating onboarding content as something a team ships rather than something one person configures.

The first is environment separation. Production and Staging exist within a single account, and the outbound webhook endpoints belong to one environment each. The release notes spell out the consequence: the two environments keep separate lists, separate signing secrets, and never see each other's traffic. A team that fires webhooks from staging into a production Slack channel or a production CRM learns this the hard way, so the separation is a real design decision rather than a label.

The second is the webhook delivery model. The v0.9.3 release added outbound webhooks covering tracked events, content publishes, and user or company changes, POSTed to an endpoint you control as they happen. Each message is signed with a per-endpoint secret. When a receiver is down, deliveries are retried across a day-long ladder. Every attempt is kept for 30 days with what was sent and what came back, and can be re-sent.

Retention with replay is the feature that matters most operationally. A webhook receiver that was down over a weekend is a data loss problem unless the sender keeps the payload, and a 30-day inspection window is long enough to debug an integration that broke silently.

The subscription model is also more flexible than a single toggle. Endpoints pick what they receive from a tree: everything, a whole family such as all tracked events or all user changes, a group, or a single topic. Families are forward compatible, so subscribing to the user family today keeps working when a new user-related topic is added later, which avoids the classic webhook breaking change.

Available topics are the tracked events the workspace already defines, with names in the form event.tracked.flow_completed and event.tracked.checklist_task_completed.

Integrations grew fast, and one release removed an old module

The release history shows the integration surface expanding quickly over a few months, in a direction that matches where this category of product is going.

Version 0.9.4 connected Usertour to analytics in both directions. Events stream to Amplitude, Heap, Mixpanel, PostHog or Segment as they happen, configured in Settings then Integrations by pasting an API key, with Heap taking an app id instead, and choosing an EU region where the provider offers one. Cohorts defined in Mixpanel or Amplitude come back as segments you can target flows and checklists at. A Zapier app triggers Zaps from Usertour events and creates users, companies and events from other apps. The release notes note there is no topic picker for event streaming, which is deliberate rather than unfinished.

That last point is a design choice with consequences. Forcing everything through means analytics providers never drift out of sync with your event catalogue, and you cannot accidentally leak an event you meant to keep internal. The cost is that a provider receives more than it needs.

The same release added a server-side path for recording events through the v2 API, so back-end code does not have to go through the browser widget, and it fixed link targets in localized content, which is the sort of bug that only surfaces once a product is translated.

It also removed a dormant legacy integration module. Changelog entries that delete old modules matter, because they are the signal that old configuration will stop working without warning.

Version 0.9.5 added a two-way HubSpot integration. Contacts map to users and companies map to companies, each pair with a match rule and its own field list per direction. Contact and company properties arrive as attributes you can target on, Usertour attributes are written back, and onboarding milestones land on HubSpot timelines. The app is listed on the HubSpot Marketplace, and one HubSpot account connects to one environment at a time.

That release also redrew team roles as Viewer, Editor, Admin and Owner, with each editor's publishing limited to the environments you tick. The upgrade note is explicit that existing Admin members become Editors on upgrade, which is the kind of migration warning that saves someone a permissions problem during a deploy.

An MCP server pointed at your own onboarding

The most distinctive feature is an MCP server, and the README documents it in unusual detail, including unedited transcripts.

The endpoint is https://mcp.usertour.io/mcp. Any MCP client discovers the authorization flow on its own. The stated behaviour is that you connect an assistant such as Claude Code, Cursor or Codex, describe the experience you want, and it builds production-ready onboarding in your project, reading your app's design system to match styling and wiring up the SDK itself if it is not installed yet.

For Claude Code specifically there is a plugin that wires both the MCP connection and the authoring skills in one step:

text
/plugin marketplace add usertour/skills
/plugin install usertour@usertour

Per-client steps exist for Cursor, Codex, VS Code and ChatChatGPT among others.

The self-hosting case is handled with a single variable, which is the right amount of ceremony:

bash
export USERTOUR_MCP_URL="https://<your-usertour-host>/mcp"

The example prompts in the README show what this looks like in practice. One asks for an onboarding flow for a Create Task feature, with three explicit requirements: the visual theme must match the existing design, the flow must be suitable for launch, and every step must provide genuine value rather than existing to pad the flow length. Another asks for a product feedback survey covering feature ratings, overall satisfaction, usability feedback, suggestions and open comments. A third asks for a single-step flow containing a modal that plays a specific YouTube video, with supporting copy written to match.

Each prompt is followed by a video of the unedited result. Publishing the failures and the successes together is more informative than a polished demo, because it shows the range of what the tool produces.

Whether an assistant authoring your onboarding is a good idea is a question the project cannot answer for you. That the interface exists at all, and can be pointed at your own instance, is the notable part. It also explains a line in the .env.example about the instance's public base URL: the OAuth and MCP methods need a correct absolute URL, which a container on localhost does not provide.

Licensing, project state, and what to check before deploying

The license situation needs attention before anything else. GitHub's metadata records NOASSERTION for the repository, meaning it could not be classified. The package.json says MIT, with the author given as Usertour, Inc. And the repository tree contains two files: LICENSE and LICENSE.enterprise.

The manifest is the clearest signal, since a company publishing an MIT-licensed package in its own repository would not do so by accident. But an enterprise licence file sitting next to the MIT one is the shape of a project where the open source edition is free and some features are reserved. Read both files before you build a commercial product on this. The version numbers also sit below 1.0, which is a reasonable signal that the licensing and feature split may still be settling.

On project state: 2,312 stars and 160 forks with 31 open issues, last pushed 2026-09-28, and the three most recent releases spanning July to September 2026 at roughly three-week intervals. That cadence indicates an actively maintained commercial-backed project rather than a hobby one, which cuts both ways: you get frequent fixes, and you get a company making decisions about which features belong in the open edition.

The language is TypeScript and the repository layout is a pnpm workspace driven by turbo, with apps/, packages/, integrations/, docs/, nginx/ and scripts/ directories at the top level. The root package.json builds through filtered turbo commands covering types, helpers, constants, emails, web, sdk and server, and lints with Biome rather than the ESLint and Prettier versions still sitting in devDependencies. A .husky directory and a prepare script running husky install indicate pre-commit hooks.

Before deploying your own instance, four things are worth confirming. That JWT_SECRET has been changed from its shipped value of test. That APP_HOMEPAGE_URL and the instance base URL are set to something reachable from the browser, not localhost. That ALLOW_PRIVATE_NETWORK_EGRESS is left false unless you genuinely need it. And that you have read both licence files rather than assuming the MIT declaration in the manifest is the whole story.

If your team needs user segmentation by company, CRM sync, and environment-aware delivery with signing and replay, this project delivers those without a sales conversation. If you need guaranteed support or a contractual uptime commitment, you are looking at a company rather than a community, and the README's Discord link is the fastest way to find out which one you are dealing with.

Editorial conclusion

Usertour is a credible self-hosted answer to the commercial onboarding tools it names in its own description, and the deciding factor for most evaluators will be licensing rather than features. The package manifest says MIT while GitHub cannot classify the repository and the tree carries a second licence file for enterprise terms, so read both before you build a commercial product on it. Technically the deployment is refreshingly ordinary: three containers, Postgres and Redis with health checks, and a single command to start. The features that matter after that are the ones a SaaS vendor would not prioritise in an open source edition: Production and Staging environments with separate webhook secrets so staging traffic cannot reach your production receiver, outbound webhooks with per-message signing and a day-long retry ladder, and HubSpot sync in both directions. If your team uses an AI coding assistant, the MCP server at the hosted endpoint is a genuine differentiator, and self hosting means pointing the same client at your own instance through one environment variable.

Frequently asked questions

What is Usertour?

Usertour is an open source user onboarding platform for building in-app product tours, checklists, launchers and surveys. The repository description positions it as an alternative to commercial tools such as Appcues, Userpilot, Userflow, Userguiding and Chameleon, and you can self host the whole thing with Docker.

How do I self host Usertour?

Copy .env.example to .env, set the required variables, then run docker compose up -d and open http://localhost:8011. The compose file starts three services: the application built from the repository Dockerfile, postgres:15-alpine, and redis:alpine. Both data services have health checks, and the app waits for them to be healthy before starting. There is also a one-click Railway deployment option.

Does Usertour support staging and production environments?

Yes. Environments such as Production and Staging are managed inside a single account, with version tracking that records who changed a flow and when. Webhook endpoints belong to one environment each, and separate environments keep separate lists and separate signing secrets without seeing each other's traffic.

What does the Usertour MCP server do?

It lets an AI coding assistant such as Claude Code, Cursor or Codex create onboarding content in your project by describing what you want. The endpoint is https://mcp.usertour.io/mcp, and the README says it reads your design system to match styling and can install the SDK if it is missing. When self hosting, point the client at your own instance by exporting USERTOUR_MCP_URL.

What license is Usertour released under?

The values conflict and you should read both files. GitHub records the repository as NOASSERTION, the package.json declares MIT with Usertour, Inc. as author, and the repository tree contains both LICENSE and LICENSE.enterprise. The manifest suggests the core is MIT, while the second file suggests some terms are reserved for enterprise use.

Which databases and services does Usertour need?

Postgres for primary data, Redis for caching and queues, and optionally S3 for object storage. The .env.example includes DATABASE_URL and DATABASE_DIRECT_URL, Redis host, port, credentials and a TLS flag, and a full set of AWS S3 variables. Authentication can be by email, GitHub, Google or OIDC SSO, and Stripe variables are present for self-hosters who want to charge their own users.

Official sources

  1. Issues
  2. Project website
  3. README
  4. Releases
  5. usertour/usertour on GitHub
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/usertour-usertour.svg)](https://hysenlabs.com/projects/usertour-usertour)