Deploy the full SecureLink platform on your own server or internal network. Your data never leaves your infrastructure. Full control over users, limits, and branding.
Why self-host?
Every scan stays on your server. Zero telemetry sent to third parties.
Protect air-gapped or corporate networks that can't reach external APIs.
Set your own scan limits, user tiers, branding, and email settings.
The platform is free to self-host. Bring your own server and API keys — no per-seat fees for the deployment itself.
Deploying SecureLink on your own server is completely free. The core platform — URL scanning, domain health checks, user accounts, and the browser extension — costs nothing to run.
Premium features (Dark Web Monitoring, Attack Surface Monitoring, AI Remediation, Priority Support) require an active Pro or Enterprise license. Users on your self-hosted instance log in with their securelinkapp.com account, and their plan tier unlocks the features they've paid for — just like the cloud version.
You need two things installed on your server:
Version 24 or newer with Docker Compose v2. Runs the app and database in isolated containers.
Pointed at your server's IP. Required for HTTPS — strongly recommended for production.
Local testing only? You can skip the domain and run on http://localhost:5000 for evaluation without HTTPS.
curl -fsSL https://get.docker.com | sh sudo usermod -aG docker $USER # allow running docker without sudo newgrp docker
Clone the repo and create your environment file from the template.
git clone https://github.com/your-org/SecureLinkApp.git cd securelink cp .env.docker.example .env
Open .env in any text editor. The file is split into sections — you only need to fill in the Required section to get started:
| Variable | Required | Description |
|---|---|---|
| SECRET_KEY | Required | Flask session secret — generate in Step 3 |
| ADMIN_SECRET_KEY | Required | Admin operations key — generate in Step 3 |
| FERNET_ENCRYPTION_KEY | Required | Encrypts sensitive data at rest — generate in Step 3 |
| APP_URL | Required | Your public URL, e.g. https://yourdomain.com |
| APP_NAME | Optional | Display name for your instance (default: SecureLink) |
| POSTGRES_USER | Required | PostgreSQL username |
| POSTGRES_PASSWORD | Required | PostgreSQL password — use a strong random value |
| SMTP_HOST / USERNAME / PASSWORD | Recommended | SMTP credentials for password resets and alerts |
Run these commands to generate cryptographically secure keys and paste the output into your .env file.
python3 -c "import secrets; print(secrets.token_hex(32))" python3 -c "import secrets; print(secrets.token_hex(32))"
python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
Never reuse keys across instances. Each deployment should have its own unique keys. Do not commit your .env file to version control.
With your .env configured, spin up the full stack — the app, PostgreSQL, and all background services start automatically.
docker compose up -d
The app binds to 127.0.0.1:5000 by default (localhost only). Check that it started correctly:
docker compose ps # all services should show "healthy" docker compose logs web # view app startup logs
The database schema is created automatically on first startup. No manual migration steps needed.
For local testing, open http://localhost:5000 in your browser. For production, continue to Step 5 to add HTTPS.
The app listens on localhost only. Nginx sits in front of it, handles HTTPS termination, and forwards traffic to the app.
sudo apt install nginx certbot python3-certbot-nginx -y
sudo cp nginx.conf /etc/nginx/sites-available/securelink sudo nano /etc/nginx/sites-available/securelink # Replace all instances of "yourdomain.com" with your actual domain sudo ln -s /etc/nginx/sites-available/securelink /etc/nginx/sites-enabled/ sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d yourdomain.com
Certbot will automatically update your Nginx config with the certificate paths and set up auto-renewal. Your instance is now accessible at https://yourdomain.com.
The nginx.conf included in the repo has rate limiting pre-configured — 30 requests/min general, 10 requests/min for auth endpoints.
The browser extension needs to point at your server instead of the default securelinkapp.com. There are two ways to do this:
Option A — Settings Panel (easiest, no code changes)
Go to chrome://extensions, enable Developer mode, click "Load unpacked", select the browser-extension/ folder.
Click the SecureLink extension icon in the toolbar, then click the ⚙ gear icon in the top-right of the popup.
Type in https://yourdomain.com and click Save. The extension will now send all scans to your server.
Option B — Bake your URL into the extension
Edit line 1 of browser-extension/background.js:
const DEFAULT_API_BASE = 'https://yourdomain.com';
Then zip the browser-extension/ folder and distribute it to your users, or publish to the Chrome Web Store.
The scanner works without any external API keys using built-in heuristics. These integrations improve detection accuracy:
| Service | Env Var | What it adds | Cost |
|---|---|---|---|
| VirusTotal | VIRUSTOTAL_API_KEY | Checks URLs against 70+ antivirus engines | Free tier (4 req/min) |
| Google Safe Browsing | GOOGLE_SAFE_BROWSING_API_KEY | Google's phishing/malware URL database | Free |
| Have I Been Pwned | HIBP_API_KEY | Email breach lookup with full breach details | ~$4/month |
| NewsAPI | NEWS_API_KEY | Security news feed on the dashboard | Free tier |
| Stripe | STRIPE_SECRET_KEY | Paid user tiers (Pro/Enterprise) | Free (pay per transaction) |
Leave Stripe keys blank to run in free-only mode — all users get free-tier access with no billing.
Run the admin manager inside the running container to create your first admin user:
docker compose exec web python manage_admins.py
Then sign in at https://yourdomain.com/login. Your instance is live.
You're done. To keep the stack running after reboots, Docker Compose's restart: unless-stopped policy handles that automatically.
Useful commands
docker compose stop # stop the stack docker compose start # start it again docker compose pull && docker compose up -d # update to latest docker compose logs -f web # tail live app logs