A fast, modular URL shortener and analytics service built with Go. Linkrr provides short URL creation, redirects, user management, and click analytics with a clean REST API.
- Overview
- Features
- Tech Stack
- Quick Start
- Configuration
- Running
- API Documentation
- Project Layout
- Development
- Testing
- Contributing
- License
Linkrr exposes REST endpoints for authentication, URL shortening, redirects, and analytics aggregation. It includes workers for email notifications and templates for login, signup, and password flows.
- Short URL creation with custom aliases
- Secure auth: signup, login, refresh tokens, password reset
- Redirect service to original URLs
- Analytics: click tracking, aggregation, global and per-link stats
- Email notifications with templated content
- Docker support for quick deployment
- Go (standard library + sqlc-generated data access)
- SQL schema and queries managed under
sql/ - Caddy for reverse proxy (via
Caddyfile) - Docker for containerized deployment
Clone or fork the GitHub repository:
# Clone
git clone https://github.com/john-ayodeji/linkrr.git
cd linkrr
# Or fork on GitHub first, then clone your forkRun the prebuilt image directly:
docker pull ayodejijohndev/linkrr:0.1.3Adjust ports as needed for your environment and proxy setup.
Notes:
- The
linkrr.example.shscript starts one Linkrr container for testing. - Caddy is used for load balancing and can accept up to three app instances (linkrr1–linkrr3). Edit
Caddyfilein the container to add more upstream servers.
Key configuration files:
Caddyfile: Reverse proxy configuration.Dockerfile: Container build instructions.internal/config/apiConfig.go: Application configuration (environment variables and defaults).
Environment variables used by the app (confirm for your deployment):
PORT: Server listen port (e.g.,8080).DB_URL: Postgres connection string (e.g.,postgres://user:pass@host:5432/db?sslmode=disable).JWT_SECRET: Secret for signing access tokens.INSTANCE_ID: Identifier for the app instance (used in logs).IPSTACK_API_KEY: API key for IP geolocation.IPSTACK_URL: Optional override for IPStack API base URL.MAILTRAP_TOKEN: API token for Mailtrap (email delivery).PLATFORM:local|dev|docker|prodto select email template paths.
Ensure Go is installed and your database is reachable.
# From repo root
go mod download
# Start the server
go run ./...You can also run the main entrypoint specifically:
go run ./main.goRecommended approach using the prebuilt image and running the orchestrator on your host/WSL:
# 1) Pull the prebuilt image
docker pull ayodejijohndev/linkrr:0.1.3
# 2) Download the example orchestrator script (from GitHub)
curl -fsSL https://raw.githubusercontent.com/john-ayodeji/linkrr/refs/heads/main/linkrr.example.sh -o linkrr.sh
# Or (PowerShell)
# Invoke-WebRequest -Uri https://raw.githubusercontent.com/john-ayodeji/linkrr/refs/heads/main/linkrr.example.sh -OutFile linkrr.sh
# 3) Edit linkrr.sh to set real secrets (DB, JWT, SMTP, etc.)
# 4) Run the orchestrator (creates network, Postgres, one Linkrr app, and Caddy)
sh linkrr.shNotes:
- The example starts one app instance (
linkrr1) for testing. - Caddy handles load balancing and can be configured to accept up to three instances (
linkrr1–linkrr3). Edit yourCaddyfileto add upstreams. - For production, run the script on your host with proper volumes, secrets management, monitoring, and backups.
Run SQL migrations inside one of the Linkrr server containers (not the DB or load balancer):
# Install goose (on host/WSL)
go install github.com/pressly/goose/v3/cmd/goose@latest
# Enter an app container shell (e.g., linkrr1)
docker exec -it linkrr1 /bin/sh
# Inside the container, run goose pointing at Postgres
export DB_URL="postgres://postgres:YOUR_PASSWORD@my-postgres:5432/linkrr?sslmode=disable"
cd /app/sql/schema
goose postgres "$DB_URL" upUse goose postgres "$DB_URL" down to roll back if needed.
See API_README.md for endpoint details, request/response formats, and examples.
A simplified overview of the structure:
internal/auth/: JWT & refresh token logicconfig/: API configurationdatabase/: sqlc-generated DB access and modelsemail_templates/: HTML templates for emailsevents_workers/: background workers (e.g., email sender)handlers/: HTTP handlers grouped by domainservices/: business logic for auth, shortener, redirect, analytics, email, usersutils/: helpers for hashing, token parsing, error handling, template rendering
sql/queries/: SQL query definitionsschema/: migration files
main.go: application entrypointRoutes.go: router setupDockerfile: container buildCaddyfile: reverse proxy config
Common tasks:
# Format
gofmt -s -w .
# Build
go build ./...
# Run
go run ./main.goIf you use sqlc, ensure it is installed and regenerate code after query/schema changes:
sqlc generateConfiguration and credentials should be provided via environment variables or .env files (avoid committing secrets).
Run unit tests:
go test ./...- Fork the repo and create a feature branch.
- Make changes with clear commit messages.
- Add/adjust tests where applicable.
- Open a pull request with a concise description of changes and rationale.