A simple, fast, and secure URL shortener service built with Django. This service allows you to create short URLs from long ones, track visits, and manage your shortened URLs through a beautiful admin interface.
I created this project because I needed a simple, lightweight, and quickly deployable URL shortener. While there are many URL shorteners available, I couldn’t find one that met my specific requirements:
After searching extensively and not finding a solution that ticked all these boxes, I decided to create my own. This project is the result of that effort - a URL shortener that’s both powerful and simple to use.
Whether you’re looking to deploy it for personal use or in a production environment, Just An URL Shortener provides all the essential features without the bloat. It’s designed to be maintainable, secure, and efficient.
After installing the project, you can access the admin interface at http://localhost:8000/admin/
(you must configure the ADMIN_URL
environment variable to change the default admin URL).
At the admin interface, you can:
View detailed analytics for each URL, including:
No technical knowledge is required - it’s designed to be user-friendly while providing powerful features under the hood.
Some screenshots of the admin interface:
The simplest way to run Ja Shortener is using Docker:
docker run -d \
-p 8080:8080 \
-e SECRET_KEY=your-secret-key \
-e SUPERUSER_USERNAME=admin \
-e SUPERUSER_EMAIL=admin@example.com \
-e SUPERUSER_PASSWORD=admin123 \
cr0hn/ja-shortener
For a more complete setup with PostgreSQL and Redis:
version: '3.8'
services:
postgres:
image: postgres:15-alpine
environment:
POSTGRES_DB: ja_shortener
POSTGRES_USER: ja_shortener
POSTGRES_PASSWORD: ja_shortener_password
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
command: redis-server --requirepass redis_password
volumes:
- redis_data:/data
ja-shortener:
image: cr0hn/ja-shortener:latest
environment:
- SECRET_KEY=your-secret-key
- DATABASE_URL=postgresql://ja_shortener:ja_shortener_password@postgres:5432/ja_shortener
- REDIS_URL=redis://:redis_password@redis:6379/0
- SUPERUSER_USERNAME=admin
- SUPERUSER_EMAIL=admin@example.com
- SUPERUSER_PASSWORD=admin123
depends_on:
- postgres
- redis
volumes:
postgres_data:
redis_data:
Save as docker-compose.yml
and run:
docker-compose up -d
Ja Shortener is available as a Helm chart on ArtifactHub.
helm repo add cr0hn https://cr0hn.github.io/helm-charts
helm repo update
# Basic installation
helm install ja-shortener cr0hn/ja-shortener
# Installation with custom values
helm install ja-shortener cr0hn/ja-shortener -f values.yaml
# Installation in a specific namespace
helm install ja-shortener cr0hn/ja-shortener -n your-namespace
Create a values.yaml
file:
# Basic configuration
replicaCount: 2
# Django configuration
django:
secretKey: "your-secret-key"
admin:
username: "admin"
email: "admin@example.com"
password: "admin123"
# Enable autoscaling
autoscaling:
enabled: true
minReplicas: 2
maxReplicas: 10
targetCPUUtilizationPercentage: 80
# Configure ingress
ingress:
enabled: true
hosts:
- host: your-domain.com
paths:
- path: /
pathType: Prefix
helm upgrade ja-shortener cr0hn/ja-shortener -f values.yaml
helm uninstall ja-shortener
The application can be configured using environment variables. Here’s a complete list of available options:
Variable | Description | Default Value | Required |
---|---|---|---|
SECRET_KEY | Django secret key for security | Random generated key | Yes |
DEBUG | Enable debug mode | False | No |
ALLOWED_HOSTS | List of allowed hosts | * | No |
CSRF_TRUSTED_ORIGINS | List of trusted origins for CSRF | http://localhost | No |
DATABASE_URL | Database connection URL | SQLite (development) | No |
REDIS_URL | Redis connection URL | None (disabled) | No |
SENTRY_DSN | Sentry DSN for error tracking | None (disabled) | No |
ADMIN_URL | Custom admin URL path | admin/ | No |
ENABLE_VISITS_TRACKING | Enable visit tracking | True | No |
GUNICORN_WORKERS | Number of Gunicorn workers | 5 | No |
GUNICORN_LOG_LEVEL | Gunicorn log level | INFO | No |
SUPERUSER_USERNAME | Superuser username | None | Yes |
SUPERUSER_EMAIL | Superuser email | None | Yes |
SUPERUSER_PASSWORD | Superuser password | None | Yes |
The application uses django-dbbackup for database backups. Here are the available configuration options:
Variable | Description | Default Value | Required |
---|---|---|---|
ENABLE_BACKUP | Enable automatic backups | False | No |
BACKUP_TYPE | Type of backup storage (s3/local) | None | Yes (if ENABLE_BACKUP=True) |
Variable | Description | Default Value | Required |
---|---|---|---|
BACKUP_ACCESS_KEY | AWS Access Key | None | Yes (for S3) |
BACKUP_SECRET_KEY | AWS Secret Key | None | Yes (for S3) |
BACKUP_BUCKET_NAME | S3 Bucket name | None | Yes (for S3) |
BACKUP_DEFAULT_ACL | S3 ACL for backups | private | No |
BACKUP_REGION | AWS Region | None | Yes (for S3) |
BACKUP_ENDPOINT_URL | Custom S3 endpoint URL | None | No |
Variable | Description | Default Value | Required |
---|---|---|---|
BACKUP_LOCATION | Local directory for backups | /data/backups | No |
Once configured, you can use the following commands to manage your backups:
# Create a database backup
python manage.py dbbackup
# Create a compressed backup
python manage.py dbbackup --compress
# Create an encrypted backup
python manage.py dbbackup --encrypt
# Create a backup with both compression and encryption
python manage.py dbbackup --compress --encrypt
# Restore the latest backup
python manage.py dbrestore
# Restore a specific backup file
python manage.py dbrestore --input-filename=backup-filename.dump
# Restore a compressed backup
python manage.py dbrestore --uncompress
# Restore an encrypted backup
python manage.py dbrestore --decrypt --passphrase=your-passphrase
# List all backups
python manage.py listbackups
# List only database backups
python manage.py listbackups --content-type=db
# List only compressed backups
python manage.py listbackups --compressed
To automate backups, you can use cron jobs or systemd timers. Here are some examples:
Add to your crontab (crontab -e
):
# Daily backup at 2 AM
0 2 * * * cd /path/to/project && python manage.py dbbackup --compress >> /var/log/backups.log 2>&1
# Weekly backup on Sunday at 3 AM
0 3 * * 0 cd /path/to/project && python manage.py dbbackup --compress --servername=weekly >> /var/log/backups.log 2>&1
version: '3.8'
services:
postgres:
image: postgres:15-alpine
environment:
POSTGRES_DB: ja_shortener
POSTGRES_USER: ja_shortener
POSTGRES_PASSWORD: ja_shortener_password
volumes:
- postgres_data:/var/lib/postgresql/data
ja-shortener:
image: cr0hn/ja-shortener:latest
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
- DATABASE_URL=postgresql://ja_shortener:ja_shortener_password@postgres:5432/ja_shortener
- ENABLE_BACKUP=True
- BACKUP_TYPE=s3
- BACKUP_ACCESS_KEY=your-aws-access-key
- BACKUP_SECRET_KEY=your-aws-secret-key
- BACKUP_BUCKET_NAME=your-backup-bucket
- BACKUP_REGION=us-east-1
command: >
sh -c "echo '0 2 * * * python manage.py dbbackup --compress >> /var/log/backups.log 2>&1' > /etc/crontabs/root &&
crond -f -l 8"
volumes:
postgres_data:
Create a service file /etc/systemd/system/ja-shortener-backup.service
:
[Unit]
Description=Ja Shortener Database Backup
After=network.target
[Service]
Type=oneshot
User=ja_shortener
WorkingDirectory=/path/to/project
ExecStart=/usr/bin/python manage.py dbbackup --compress
Create a timer file /etc/systemd/system/ja-shortener-backup.timer
:
[Unit]
Description=Run Ja Shortener backup daily
[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true
[Install]
WantedBy=timers.target
Enable and start the timer:
sudo systemctl enable ja-shortener-backup.timer
sudo systemctl start ja-shortener-backup.timer
When deploying in a High Availability environment, please consider the following:
REDIS_URL=redis://your-redis-host:6379/0
SECRET_KEY
secure and rotate it periodicallyGUNICORN_WORKERS
based on your server’s CPU coresBefore going to production, ensure you have:
This project is licensed under the Functionary Source License (FSL). See the LICENSE file for details.
This project is available under the Functionary Source License (FSL). You are free to:
However, you are NOT allowed to:
If you need to use this software in a way that conflicts with these terms, please contact me at cr0hn
We welcome contributions! Please see our Contributing Guide for details.