Skip to content

Latest commit

 

History

6 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Corto

License: AGPL v3

A modern URL shortener that stays yours. Free, open source, and easy to self-host in several ways. Password-protect a link, give it an expiry date or a click limit, run your own ads on it, and read the analytics from the dashboard — or skip the dashboard entirely and drive everything through the API. Secure by design, and contributions are welcome.

Deploy it with the databases wired up for you:

Deploy to Render Deploy on Railway

Render reads render.yaml and provisions PostgreSQL and a Key Value store alongside the app; Railway's template does the same with PostgreSQL and Redis. Both build the all-in-one standalone image, so one service answers /, /api/*, and /<slug>. Details and self-hosting: One-click deploy.

Cloudflare Workers and Vercel are not supported — the API needs TCP sockets to PostgreSQL and Redis plus Bun's password hashing. Why.

Documentation

Guide What it covers
One-click deploy Render, Railway, and the all-in-one image for a bare VPS.
Run locally Requirements, environment variables, migrations, and development commands.
Deploy on Dokploy Production deployment with managed PostgreSQL and Redis.
Deploy on Coolify Git-backed Docker Compose deployment on Coolify.
Deployment templates Dokploy and Coolify Compose templates bundled with the repository.
Contributing Fork-based workflow and pull request requirements.

Architecture

  • apps/api: Hono on Bun, Drizzle, Better Auth, and a router → controller → handler flow. Port 8787.
  • apps/web: landing page, dashboard, analytics, and API documentation in Next.js. Port 3000.
  • Caddyfile: the only HTTP entrypoint on port 80; proxies /api/* to the API, GUI routes to Next.js, and /<slug> to the API.
  • packages/ui: shared shadcn/ui components.
  • PostgreSQL stores users, links, API keys, and events. Redis uses REDIS_PREFIX (default corto:) for cache and redirects.

PostgreSQL and Redis are not part of the root Compose stack. Connect shared instances with DATABASE_URL and REDIS_URL. In Dokploy, all root services use DOCKER_NETWORK (default dokploy-network) and unique aliases to avoid DNS collisions with other projects.

Quick start

Requires Bun 1.3.14 plus reachable PostgreSQL and Redis instances.

git clone https://github.com/logical-tech/Corto.git
cd Corto
cp .env.example .env
bun install --frozen-lockfile
bun run --cwd apps/api migrate
bun run dev

Open http://localhost:3000. During development, Next.js proxies /api/* to Hono; the health check is http://localhost:3000/api/health.

Localhost values in .env.example apply when Bun runs on your machine. When you run containers, use PostgreSQL and Redis hostnames reachable from the containers.

API

The web app exposes a guide at /docs; the public machine-readable schema is /api/openapi.json.

curl -X POST "$NEXT_PUBLIC_APP_URL/api/v1/links" \
  -H "x-api-key: $CORTO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/guide","slug":"guide"}'

Advertising is configured per link owner through GET and PATCH /api/v1/advertising. AdsTerra accepts a supported banner preset plus either the invoke.js URL or the entire copied snippet. By default, the interstitial runs its countdown and then reveals Continue to site; automaticRedirect: true redirects when it ends. adFree: true excludes one link.

Verify the service without authentication:

curl -f "$NEXT_PUBLIC_APP_URL/api/health"

Commands

bun run dev                         # all workspaces in watch mode
bun run build                       # Turbo production build
bun run lint                        # lint every workspace
bun run typecheck                   # TypeScript checks
bun run --cwd apps/api test         # API tests
bun run --cwd apps/api migrate      # PostgreSQL migrations
bun run --cwd apps/api db:generate  # create Drizzle migrations from the schema

Docker

The same multi-stage Dockerfile creates caddy, api, web, and standalone targets and installs the monorepo once. Application images run as a non-root user. standalone is the last stage, so a plain docker build . produces the all-in-one image the one-click hosts deploy.

cp .env.example .env
# Set public URLs, secrets, and external services first.
docker compose -f compose.yaml up -d --build

# API, redirects, and tracking only; no GUI:
docker compose -f compose-nogui.yaml up -d --build

Only Caddy is exposed on the Docker network, at port 80; web:3000 and api:8787 remain private. In Dokploy, attach the only domain to the caddy service on port 80.

License

GNU Affero General Public License v3.0. Use it, self-host it, change it — keep the copyright notice, and publish your source if you run a modified version as a network service.

About

Corto is a mordern url shortner, free, open source, self hostable in many easy ways. Corto has many features such has: link passwords, link expiry, link view limits, custom ads, analytics and more. Secure by design and headless too if you dont need the web interface! Contributing is open

Topics

Resources

Contributing

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages