Kutt: a self-hosted URL shortener with custom domains and zero configuration
Free Modern URL Shortener.
At a glance
- What is it?
- Kutt is a Node.js URL shortener built for self-hosting, with SQLite, Postgres and MySQL backends, custom domains and a REST API. The setup is genuinely simple, but the defaults assume a single instance and the documentation leaves several operational questions open.
- Who is it for?
- Kutt suits teams that want a URL shortener on their own domain without running a build pipeline: Node.js 20 or above, npm install, npm run migrate, npm start, or docker compose up with the shipped compose files. It is a poor fit if you need horizontal scaling across multiple app instances, because the default SQLite file and the in-process job queue in bull both assume a single writer, and the README does not describe a shared-queue configuration.
- 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 27 days ago.
- What is it written in?
- Mainly JavaScript, 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 Kutt solves, and who ends up running it
Short links are easy to generate and annoying to own. A hosted shortener gives you a link on someone else's domain, which means the link's lifespan is tied to a service you do not control. Kutt's answer is to run the whole thing yourself: the README describes it as a modern URL shortener with custom domain support, where you create and edit links, view statistics and manage users. The key features list states the project was created with self-host in mind, and the first two bullets are zero configuration needed and easy setup with no build step. That framing tells you the intended user. It is not someone building a link platform as a product. It is an individual or a small team that already has a server and a domain, and wants short links on that domain without standing up a frontend build chain. The feature set matches that scope: custom URLs, passwords, descriptions and expiration times per link, private statistics, an admin page for users and links, and optional OpenID Connect login. The registration and anonymous-link switches matter more than they look. A public shortener attracts spam, and being able to disable registration and anonymous link creation is the difference between a tool you can leave exposed and one you cannot.
The request path: Node, knex and a database you choose
The architecture is a single Node process. package.json sets main to ./server/server.js, and both the dev and start scripts run that file directly, with dev adding --watch-path flags for ./server and ./custom. There is no bundler and no transpile step, which is what the no build step claim means in practice. Persistence goes through knex, and the configuration table lists the supported clients: pg or pg-native for Postgres, mysql2 for MySQL or MariaDB, sqlite3 and better-sqlite3 for SQLite. The default is better-sqlite3, so a fresh checkout writes to a local file. Schema changes are knex migrations, run through npm run migrate, and the Dockerfile's CMD runs npm run migrate before npm start, so a container migrates itself on boot. Everything else is configuration by environment variable. The README states all variables are optional except JWT_SECRET, which is required on production, and that any variable can be supplied from a file by appending _FILE, so JWT_SECRET_FILE=/path/to/secret_file works. That last detail is the kind of thing that makes a container deployment less painful, because secrets do not have to sit in the compose file. Two dependencies hint at the operational shape. bull is a Redis-backed job queue, and rate-limit-redis pairs with ioredis, so rate limiting can be shared through Redis when REDIS_ENABLED is set. Without Redis, both are per-process. The README does not describe how the queue behaves across multiple app instances.
Installing Kutt from source and shortening your first link
The README gives Node.js version 20 or above as the only prerequisite, with SQLite as the default database. The source path is four commands. Clone the repository or download the latest zip from the releases page, then run:
npm install
npm run migrate
npm startThe README lists `npm run dev` for development instead of `npm start`, and notes the app prompts you to create an admin account the first time it starts. The migrate step is separate on purpose: the Dockerfile chains it into the container command, but a source install runs it yourself.
For Docker, the README says to run this from the root directory:
docker compose upThe default docker-compose.yml builds the image from the local Dockerfile, mounts a named volume at /var/lib/kutt, sets DB_FILENAME to /var/lib/kutt/data.sqlite, and publishes port 3000. The Dockerfile is based on node:22-alpine and its CMD is npm run migrate && npm start, so migrations run on every container start. The README also lists three other compose files: docker-compose.sqlite-redis.yml, docker-compose.postgres.yml and docker-compose.mariadb.yml, started with docker compose -f <file_name> up. The Postgres file requires REDIS_ENABLED, DB_PASSWORD, DB_NAME and DB_USER; the MariaDB file adds DB_PORT to that list. Once the app is up, the configuration table says DEFAULT_DOMAIN defaults to localhost:3000 and PORT defaults to 3000, so a local instance is reachable at http://localhost:3000 without any environment file at all.
Where Kutt's defaults stop being enough
The default database is the first constraint. SQLite is a single file, and the shipped docker-compose.yml mounts it as a named volume attached to one service. Anything that runs more than one copy of the server against that file is outside what the default configuration describes. Postgres and MySQL are supported, and the configuration table exposes DB_POOL_MIN for connection pooling, but the README does not document a multi-instance deployment, session sharing or a shared bull queue. If your requirement is a shortener behind a load balancer with several app replicas, treat that as unverified rather than supported.
The second constraint is the proxy setting. TRUST_PROXY defaults to true, and the README is explicit about the failure mode: if you are not using a proxy server, set it to false, otherwise users can override their IP address. That is a real security-relevant default for anyone running the container directly on a public port, which the shipped compose file does by publishing 3000:3000.
The third is the documentation boundary. The README covers setup, Docker, configuration, themes, API and integrations, and it points to docs.kutt.to for the API. It does not document rollback, database migration reversal, or upgrade procedures between releases. The repository does ship knex migration files under db/, so the mechanism exists, but the operational guidance for undoing a bad migration is not in the README.
Finally, the domain warning. The README states in a warning block that kutt.it is not owned by the project and could be a phishing site, and directs users to kutt.to. Anyone following an older tutorial that still points at kutt.it is being sent somewhere the maintainers do not control.
Kutt against YOURLS and Shlink
The related searches around this project include YOURLS and Shlink, and the difference is mostly about runtime and data model. YOURLS is the long-standing PHP option: PHP plus MySQL, with a plugin ecosystem that has accumulated over many years. If your infrastructure is already PHP and MySQL, that is a shorter path than adding a Node runtime. Kutt's stack is Node.js 20 or above with knex over SQLite, Postgres or MySQL, which means the database is a choice rather than a requirement, and the default install needs no separate database server at all.
Shlink is the other close comparison. It is a PHP URL shortener with an API-first design, and its documentation is built around consuming that API rather than around a web UI. Kutt ships a web interface with an admin page, link management and statistics, plus a REST API documented at docs.kutt.to. If your shortener is a backend for another system, an API-first tool fits that shape. If you want a page where a person logs in and manages links, Kutt's UI is the point.
One thing all three share: they are self-hosted, so the domain, the data and the uptime are yours. The choice between them is mostly which runtime you already operate and whether you want a UI as the primary interface or as an addition to an API.
Licence and the cost of staying current
Kutt is MIT licensed, per the repository metadata and the LICENSE file at the root. MIT is permissive: you can run it commercially, modify it and redistribute it, provided the copyright notice and permission notice are preserved. The README's only licence-related claim is the badge linking to the LICENSE file. Nothing suggests a separate commercial tier, a source-available clause or a contributor licence agreement that would change how you can use the code. This is a description of the licence text, not legal advice; if you plan to redistribute a modified Kutt, read the LICENSE file yourself.
Upgrade cost is the more interesting question. The last push to the repository was on 2026-09-03, and the most recent release listed is v3.2.6 from 2026-07-05, following v3.2.5 and v3.2.4 in May 2026. Releases arrive in small increments rather than as a steady stream. Because migrations are knex files and the Docker image runs npm run migrate on every start, upgrading a container means pulling a new image and restarting; the migration runs automatically. What the README does not give you is a downgrade path. If a migration changes a column in a way you dislike, the documentation does not describe how to reverse it. The practical cost of running Kutt is therefore not the install, which is short, but the fact that you are responsible for backing up the database before an upgrade, since the project does not document the reverse operation.
Editorial conclusion
Kutt suits teams that want a URL shortener on their own domain without running a build pipeline: Node.js 20 or above, npm install, npm run migrate, npm start, or docker compose up with the shipped compose files. It is a poor fit if you need horizontal scaling across multiple app instances, because the default SQLite file and the in-process job queue in bull both assume a single writer, and the README does not describe a shared-queue configuration. Before adopting it, verify three things in your own environment: that the database client you pick is actually installed (the configuration table notes pg-native and sqlite3 are not installed by default), that TRUST_PROXY matches your deployment, since the README warns users can override their IP address when it is wrong, and that JWT_SECRET is set to a long random value, because it is the only variable the README marks as required in production.
Frequently asked questions
What is Kutt?
Kutt is a modern URL shortener with support for custom domains, according to the README. It lets you create and edit links, view statistics and manage users, and it is designed to be self-hosted.
What are some open source URL shorteners?
Kutt is one, MIT licensed and written in JavaScript for Node.js 20 or above. The related searches around it also name YOURLS, Shlink and Chhoto URL as alternatives in the same category.
How can I shorten URLs with a custom domain in Kutt?
The README lists custom domain support as a key feature, and the configuration table exposes DEFAULT_DOMAIN, which defaults to localhost:3000 and should be set to the domain the app runs on. Individual links also support custom URLs, passwords, descriptions and expiration times.
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/thedevs-network-kutt)