Self-Hosting Guide
Deploy LinkForty Core on your own infrastructure with complete control over your data.
This guide covers deploying LinkForty Core - the open-source deeplink engine. Core provides a REST API for link management, device-specific redirects, click analytics, deferred deep linking, QR codes, and webhooks. It does not include the Cloud dashboard, team management, or billing features. For a managed experience with a UI, see the Quick Start for LinkForty Cloud.
What You Get
LinkForty Core is a Fastify server backed by PostgreSQL (with optional Redis caching) that provides:
- REST API for creating, reading, updating, and deleting links
- Smart redirect routing with device detection (iOS, Android, web)
- Click analytics with geolocation, device type, and UTM tracking
- Deferred deep linking and fingerprint-based attribution
- QR code generation (PNG and SVG)
- Webhook notifications for real-time events
- Social previews via Open Graph tags
- iOS Universal Links and Android App Links support (
.well-knownendpoints)
All interactions with Core happen via its API. There is no built-in UI - you integrate it into your own application or use it as a standalone API service.
Why Self-Host?
Control and Flexibility:
- No per-install fees - unlimited scaling
- No vendor lock-in
- Deploy anywhere (AWS, GCP, Azure, DigitalOcean, on-premise)
Data Privacy:
- Complete data ownership
- GDPR/CCPA compliance on your terms
- No third-party data sharing
Customization:
- Full source code access (AGPL-3.0 license)
- Custom features and integrations
- Modify attribution logic for your use case
System Requirements
Minimum Specifications
Server:
- CPU: 2 cores
- RAM: 4 GB
- Storage: 20 GB SSD
- OS: Ubuntu 20.04+ / Debian 11+ / RHEL 8+
Software:
- Docker 20.10+ and Docker Compose 2.0+
- OR Node.js 18+ with PostgreSQL 14+ and Redis 7+ (optional)
Expected Load Capacity:
- ~1,000 requests/minute
- ~100,000 links
- ~1M clicks/month
Recommended Specifications (Production)
Server:
- CPU: 4 cores
- RAM: 8 GB
- Storage: 100 GB SSD
- OS: Ubuntu 22.04 LTS
Expected Load Capacity:
- ~10,000 requests/minute
- ~1M links
- ~10M clicks/month
Cloud Provider Options
Recommended instance types for minimum specifications:
| Provider | Instance Type | Specs |
|---|---|---|
| DigitalOcean | Droplet (Basic) | 2 CPU, 4GB RAM |
| AWS | t3.medium | 2 CPU, 4GB RAM |
| Google Cloud | e2-medium | 2 CPU, 4GB RAM |
| Hetzner | CX21 | 2 CPU, 4GB RAM |
| Vultr | Regular Performance | 2 CPU, 4GB RAM |
Installation Methods
Choose your deployment method:
- Method 1: Docker Compose (Recommended) - Fastest and easiest. Everything configured out of the box.
- Method 2: npm Package - Install Core as an npm dependency in your own Node.js application.
- Method 3: Manual Installation - For custom setups or when Docker isn't available.
- Method 4: Kubernetes - For enterprise deployments with high availability.
Method 1: Docker Compose (Recommended)
Step 1: Download Docker Compose File
mkdir linkforty && cd linkforty
curl -O https://raw.githubusercontent.com/linkforty/core/main/docker-compose.yml
Step 2: Configure Environment
Create a .env file:
# Database
POSTGRES_DB=linkforty
POSTGRES_USER=linkforty
POSTGRES_PASSWORD=your-strong-password
POSTGRES_PORT=5432
# Redis (optional but recommended for caching)
REDIS_URL=redis://redis:6379
REDIS_PORT=6379
# Server
NODE_ENV=production
PORT=3000
HOST=0.0.0.0
LINKFORTY_PORT=3000
# CORS - set to your frontend domain
CORS_ORIGIN=*
# Optional: behind a CDN/proxy (e.g. Cloudflare), read the real client IP
# from the proxy's header so attribution stays accurate. Only enable when
# the origin is reachable ONLY through that proxy. See the env-var reference.
# TRUSTED_CLIENT_IP_HEADER=cf-connecting-ip
# Optional: JWT secret for authentication
# JWT_SECRET=your-super-secret-jwt-key
# Optional: Custom domain for short links
# SERVICE_DOMAIN=yourdomain.com
# Optional: iOS Universal Links
# IOS_TEAM_ID=ABC123XYZ
# IOS_BUNDLE_ID=com.yourcompany.yourapp
# Optional: Android App Links
# ANDROID_PACKAGE_NAME=com.yourcompany.yourapp
# ANDROID_SHA256_FINGERPRINTS=AA:BB:CC:DD:...
Security Note: Generate strong secrets with:
openssl rand -hex 32
Step 3: Start Services
docker compose up -d
This starts three containers:
- linkforty: The Core Fastify API server (port 3000)
- postgres: PostgreSQL database (port 5432)
- redis: Redis cache (port 6379)
The database schema is automatically initialized on first startup.
Step 4: Verify Installation
# Check all services are running
docker compose ps
# Test the API
curl http://localhost:3000/health
You should see a healthy response. The API is now available at http://localhost:3000.
Step 5: Create Your First Link
curl -X POST http://localhost:3000/api/links \
-H "Content-Type: application/json" \
-d '{
"userId": "user-1",
"originalUrl": "https://example.com",
"title": "My First Link",
"iosUrl": "https://apps.apple.com/app/your-app/id123456789",
"androidUrl": "https://play.google.com/store/apps/details?id=com.yourapp",
"webFallbackUrl": "https://example.com",
"utmParameters": {
"source": "twitter",
"medium": "social",
"campaign": "launch"
}
}'
The response will include a short_code you can use for redirects: http://localhost:3000/{shortCode}
Step 6: Set Up SSL (Required for Production)
Option A: Using Let's Encrypt (Free)
Install Certbot and Nginx:
sudo apt-get update
sudo apt-get install certbot python3-certbot-nginx nginx
Configure Nginx as a reverse proxy:
# /etc/nginx/sites-available/linkforty
server {
listen 80;
server_name yourdomain.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name yourdomain.com;
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
# API and redirect routes
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $host;
}
}
Enable site and generate certificate:
sudo ln -s /etc/nginx/sites-available/linkforty /etc/nginx/sites-enabled/
sudo certbot --nginx -d yourdomain.com
sudo systemctl reload nginx
Option B: Using Cloudflare (Free SSL + CDN)
- Point your domain to Cloudflare nameservers
- Enable Full (strict) SSL mode in Cloudflare dashboard
- Create an origin certificate in Cloudflare
- Install the origin certificate on your server
- Enable Always Use HTTPS in Cloudflare
Method 2: npm Package
Install LinkForty Core as a dependency in your own Node.js application:
npm install @linkforty/core
Basic Server
import { createServer } from '@linkforty/core';
async function start() {
const server = await createServer({
database: {
url: 'postgresql://linkforty:password@localhost:5432/linkforty',
},
redis: {
url: 'redis://localhost:6379',
},
});
await server.listen({ port: 3000, host: '0.0.0.0' });
console.log('LinkForty Core running on http://localhost:3000');
}
start();
Using Individual Route Handlers
You can also register only specific routes in your own Fastify application:
import Fastify from 'fastify';
import { initializeDatabase, redirectRoutes, linkRoutes, analyticsRoutes } from '@linkforty/core';
const fastify = Fastify();
// Initialize database separately
await initializeDatabase({ url: 'postgresql://localhost/linkforty' });
// Register only the routes you need
await fastify.register(redirectRoutes);
await fastify.register(linkRoutes);
await fastify.register(analyticsRoutes);
await fastify.listen({ port: 3000 });
This gives you maximum flexibility to integrate LinkForty into an existing application.
Method 3: Manual Installation
Step 1: Install Dependencies
# Update system
sudo apt-get update && sudo apt-get upgrade -y
# Install Node.js 18+
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt-get install -y nodejs
# Install PostgreSQL 14+
sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
sudo apt-get update
sudo apt-get install -y postgresql-14
# Install Redis 7+ (optional but recommended)
sudo apt-get install -y redis-server
Step 2: Configure PostgreSQL
sudo -u postgres psql << EOF
CREATE DATABASE linkforty;
CREATE USER linkforty WITH PASSWORD 'your-strong-password';
GRANT ALL PRIVILEGES ON DATABASE linkforty TO linkforty;
\q
EOF
Step 3: Clone and Build
git clone https://github.com/linkforty/core.git
cd core
npm install
npm run build
Step 4: Configure Environment
cp .env.example .env
nano .env
Update the environment variables:
DATABASE_URL=postgresql://linkforty:your-strong-password@localhost:5432/linkforty
REDIS_URL=redis://localhost:6379
NODE_ENV=production
PORT=3000
HOST=0.0.0.0
CORS_ORIGIN=*
Step 5: Run Database Migrations
npm run migrate
Step 6: Set Up systemd Service
Create a service file for automatic startup:
sudo nano /etc/systemd/system/linkforty.service
[Unit]
Description=LinkForty Core
After=network.target postgresql.service redis.service
[Service]
Type=simple
User=www-data
WorkingDirectory=/opt/linkforty/core
Environment="NODE_ENV=production"
EnvironmentFile=/opt/linkforty/core/.env
ExecStart=/usr/bin/node dist/index.js
Restart=on-failure
[Install]
WantedBy=multi-user.target
Enable and start the service:
sudo systemctl daemon-reload
sudo systemctl enable linkforty
sudo systemctl start linkforty
# Check status
sudo systemctl status linkforty
Method 4: Kubernetes
Prerequisites
- Kubernetes cluster (EKS, GKE, AKS, or self-hosted)
- kubectl configured
- Helm 3+
Step 1: Add Helm Repository
helm repo add linkforty https://charts.linkforty.com
helm repo update
Step 2: Create Values File
# values.yaml
replicaCount: 3
image:
repository: linkforty/core
tag: "latest"
pullPolicy: IfNotPresent
service:
type: LoadBalancer
port: 80
ingress:
enabled: true
className: nginx
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
hosts:
- host: linkforty.yourdomain.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: linkforty-tls
hosts:
- linkforty.yourdomain.com
postgresql:
enabled: true
auth:
username: linkforty
password: your-strong-password
database: linkforty
primary:
persistence:
size: 20Gi
redis:
enabled: true
auth:
enabled: false
master:
persistence:
size: 8Gi
env:
NODE_ENV: production
CORS_ORIGIN: "*"
resources:
limits:
cpu: 1000m
memory: 1Gi
requests:
cpu: 500m
memory: 512Mi
autoscaling:
enabled: true
minReplicas: 3
maxReplicas: 10
targetCPUUtilizationPercentage: 80
Step 3: Install with Helm
helm install linkforty linkforty/core -f values.yaml
Step 4: Verify Deployment
kubectl get pods
kubectl get services
kubectl logs -l app=linkforty
Post-Installation Setup
1. Configure Custom Domain
Update DNS records to point to your server:
A yourdomain.com → your-server-ip
Then set the SERVICE_DOMAIN environment variable:
SERVICE_DOMAIN=yourdomain.com
2. Configure Mobile App Links
For iOS Universal Links and Android App Links, set the appropriate environment variables:
# iOS Universal Links
IOS_TEAM_ID=ABC123XYZ
IOS_BUNDLE_ID=com.yourcompany.yourapp
# Android App Links
ANDROID_PACKAGE_NAME=com.yourcompany.yourapp
ANDROID_SHA256_FINGERPRINTS=AA:BB:CC:DD:...
Core will automatically serve the .well-known/apple-app-site-association and .well-known/assetlinks.json files. See iOS Universal Links and Android App Links for the full setup.
3. Preserve Client IP Behind a CDN
If you run LinkForty behind Cloudflare, a CDN, or a load balancer, the connection IP Core sees is the proxy's — not the visitor's. Since attribution fingerprinting keys on the client IP, this hurts match accuracy. Point Core at your proxy's authoritative client-IP header:
TRUSTED_CLIENT_IP_HEADER=cf-connecting-ip # Cloudflare
Only enable this when your origin is reachable exclusively through that proxy — otherwise the header can be spoofed. See the Environment Variables reference for details.
4. Set Up Backups
PostgreSQL Backup Script
#!/bin/bash
# /opt/linkforty/backup.sh
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="/backups/linkforty"
mkdir -p $BACKUP_DIR
# Backup database
docker compose exec -T postgres pg_dump -U linkforty linkforty | gzip > "$BACKUP_DIR/linkforty_$DATE.sql.gz"
# Keep only last 30 days
find $BACKUP_DIR -name "*.sql.gz" -mtime +30 -delete
# Upload to S3 (optional)
# aws s3 cp "$BACKUP_DIR/linkforty_$DATE.sql.gz" s3://your-backup-bucket/
Add to crontab:
crontab -e
# Daily backup at 2 AM
0 2 * * * /opt/linkforty/backup.sh
5. Set Up Monitoring
Using Docker Stats
docker stats linkforty-linkforty-1 linkforty-postgres-1
Using Prometheus + Grafana
# Add to docker-compose.yml
prometheus:
image: prom/prometheus
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
ports:
- "9090:9090"
grafana:
image: grafana/grafana
ports:
- "3002:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
6. Configure Firewall
# Allow HTTP/HTTPS
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# Allow SSH
sudo ufw allow 22/tcp
# Enable firewall
sudo ufw enable
Updating LinkForty
Docker Compose
cd /opt/linkforty
# Pull latest image
docker compose pull
# Restart with new image
docker compose up -d
Manual Installation
cd /opt/linkforty/core
# Pull latest code
git pull origin main
# Update dependencies
npm install
# Rebuild
npm run build
# Run migrations (if needed)
npm run migrate
# Restart service
sudo systemctl restart linkforty
Troubleshooting
Server Won't Start
# Check logs (Docker)
docker compose logs linkforty
# Check logs (systemd)
sudo journalctl -u linkforty -f
# Common issues:
# 1. Database not ready - wait 10 seconds and retry
# 2. Redis not accessible - check REDIS_URL (Redis is optional, remove REDIS_URL to skip)
# 3. Port 3000 in use - change PORT in .env
Database Connection Errors
# Test PostgreSQL connection (Docker)
docker compose exec postgres psql -U linkforty -d linkforty -c "SELECT 1;"
# Check DATABASE_URL format:
# postgresql://username:password@host:port/database
High Memory Usage
# Increase PostgreSQL shared_buffers
docker compose exec postgres psql -U linkforty -d linkforty -c "ALTER SYSTEM SET shared_buffers = '256MB';"
docker compose restart postgres
# Increase Node.js memory limit
# Add to environment in docker-compose.yml:
# NODE_OPTIONS: "--max-old-space-size=2048"
Redis Connection Errors
Redis is optional. If you don't need caching, remove the REDIS_URL environment variable and Core will fall back to direct database queries. If you do use Redis and see connection errors:
# Check Redis is running
docker compose exec redis redis-cli ping
# Should return: PONG
Production Checklist
Before going live:
- SSL certificate installed and working
- Strong database password set (not the default
changeme) - Database backups configured (daily minimum)
- Firewall rules configured
- Monitoring set up
- DNS records pointing to server
- Rate limiting configured (if needed)
- Test link created and redirect working
- Health check endpoint responding (
/health) - CORS configured for your frontend domain
API Reference
Once Core is running, all interactions happen through the REST API:
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/links | Create a new link |
| GET | /api/links?userId=... | List all links for a user |
| GET | /api/links/:id?userId=... | Get a specific link |
| PUT | /api/links/:id?userId=... | Update a link |
| DELETE | /api/links/:id?userId=... | Delete a link |
| GET | /api/analytics/overview?userId=... | Analytics overview |
| GET | /api/analytics/links/:id?userId=... | Link-specific analytics |
| GET | /:shortCode | Redirect (public, no auth) |
For full API documentation, see the API Reference.
Getting Help
- API Reference - Complete API documentation
- GitHub Repository - Source code and releases
- Report Issues - Bug reports
- GitHub Discussions - Community support
Next Steps
- Environment Variables - Full configuration reference
- API Authentication - Learn how to authenticate API requests
- Mobile SDK Integration - Set up deep linking in your app
- Webhook Configuration - Real-time event notifications
- Cloud vs Self-Hosted - Compare with the managed Cloud option