Lynx is an open-source, self-hosted link page manager: public profile page, admin panel, SQLite storage, theme editor, analytics, and secure admin access in one small app.
- π¬ Demo
- β¨ Features
- π Security
- π Quick Start
- βοΈ Configuration
- π³ Docker
- βοΈ Railway
- π Changelog
- π License
- π Public page: https://lynx-demo.paoloronco.it
- π Admin panel: https://lynx-demo.paoloronco.it/admin
- π€ Username:
admin - π Password:
ChangeMe123!
- π Admin panel: edit profile, links, theme, password, and reset options from one dashboard.
- π€ Public profile: name, bio, avatar, social links, page title, meta description, favicon, and footer text.
- π Flexible cards: classic links, text cards, bulleted/grouped content, separators, icons, emojis, and images.
- π¨ Theme control: colors, gradients, fonts, spacing, radius, blur, glow, button styles, link styles, and custom CSS.
- π Live preview: check the public page while editing.
- π Analytics: click tracking with an admin chart.
- π Scheduling: show or hide links by date range.
- π Visibility toggles: hide links without deleting them.
- π± Mobile-friendly editing: responsive UI and touch drag-and-drop ordering.
- π¦ Import/export: backup and restore links and themes as JSON.
- π Standalone storage: SQLite by default, no Firebase or Supabase required.
- π Optional HTTPS: enable a self-signed HTTPS listener with
ENABLE_HTTPS=true.
- Passwords are hashed with
bcryptjsusing 12 salt rounds. - Sessions use signed JWTs with a 12-hour expiry.
- The frontend stores JWTs encrypted in
localStoragewith AES-GCM. - SQLite access uses parameterized queries.
- API, auth, login, reset, and SPA routes are rate-limited.
- Docker startup requires
JWT_SECRETto avoid unstable sessions after restart. - Optional
RESET_TOKENsupports token-protected recovery if you are locked out.
Pick the path that matches what you want to do.
β‘ Run Lynx locally
Use this if you want to try Lynx on your machine with a production-style flow: the React app is built first, then the Express server serves both the frontend and the API.
Requirements
- Node.js
^20.19.0or>=22.12.0 - npm
- Git
1. Clone the repository
git clone https://github.com/paoloronco/Lynx.git
cd Lynx/LYNX2. Install dependencies
npm ci
npm run install:server3. Build and start
npm run startnpm run start runs the frontend build and then starts the backend server.
Open:
- π Public page: http://localhost:3001
- π Admin panel: http://localhost:3001/admin
- β€οΈ Health check: http://localhost:3001/health
On the first admin visit, Lynx asks you to create the admin password. The username is always admin.
Local data is stored in LYNX/server/lynx.db unless you set DATA_DIR.
π§βπ» Development mode
Use this when you are editing the code and want frontend hot reload.
Development mode uses two running processes:
- Backend/API: Express server on http://localhost:3001
- Frontend: Vite dev server on http://localhost:8080
The Vite server gives you hot reload for React and proxies /api requests to the Express server.
1. Install dependencies once
cd Lynx/LYNX
npm ci
npm run install:server2. Start the backend in the first terminal
cd Lynx/LYNX
npm run server:dev3. Start the frontend in the second terminal
cd Lynx/LYNX
npm run devKeep both terminals open while developing.
Open:
- π Frontend: http://localhost:8080
- π Admin panel: http://localhost:8080/admin
- β€οΈ API health check: http://localhost:3001/health
Stop either process with Ctrl+C.
π³ Quick Docker run
Use this if you want the fastest container-based setup.
From the repository root:
docker compose up -dThe included docker-compose.yml uses:
- image:
paueron/lynx:latest - port:
8080 - volume:
./lynx-data:/app/data
Before exposing the app, replace the sample JWT_SECRET in docker-compose.yml.
Open:
- π Public page: http://localhost:8080
- π Admin panel: http://localhost:8080/admin
Environment variables
| Variable | Default | Notes |
|---|---|---|
JWT_SECRET |
random outside Docker | Required in Docker. Use a long random value in production. |
NODE_ENV |
unset | Set to production for production deployments. |
PORT |
3001 local, 8080 Docker |
HTTP server port. |
DATA_DIR |
LYNX/server local, /app/data Docker |
Stores lynx.db and persistent data. |
ENABLE_HTTPS |
false |
Set to true or 1 for self-signed HTTPS. |
SSL_PORT |
8443 |
HTTPS port. |
FRONTEND_URL |
same-origin mode | Optional dev CORS/CSP origin, e.g. http://localhost:8080. |
RESET_TOKEN |
unset | Enables token-protected reset endpoints. Use at least 32 characters. |
πΎ Data persistence
- Local: data is stored in
LYNX/server/lynx.dbunlessDATA_DIRis set. - Docker: mount
/app/dataso the database and uploads survive container updates.
Docker Compose
From the repository root:
docker compose up -dThe included docker-compose.yml uses:
- image:
paueron/lynx:latest - port:
8080 - volume:
./lynx-data:/app/data
Before exposing the app, replace the sample JWT_SECRET in docker-compose.yml.
Docker CLI
docker pull paueron/lynx:latest
docker run -d --name lynx \
-p 8080:8080 \
-e NODE_ENV=production \
-e PORT=8080 \
-e JWT_SECRET="replace-with-a-long-random-secret" \
-v lynx_data:/app/data \
paueron/lynx:latestOpen:
- π Public page: http://localhost:8080
- π Admin panel: http://localhost:8080/admin
Optional HTTPS
docker run -d --name lynx \
-p 8080:8080 \
-p 8443:8443 \
-e NODE_ENV=production \
-e PORT=8080 \
-e JWT_SECRET="replace-with-a-long-random-secret" \
-e ENABLE_HTTPS=true \
-v lynx_data:/app/data \
paueron/lynx:latestThen open https://localhost:8443. The browser will warn because the certificate is self-signed.
Deploy on Railway
Railway can deploy the repository using the root Dockerfile.
- Create a new Railway project from the GitHub repository.
- Let Railway use the Dockerfile in the repository root.
- Add:
NODE_ENV=production
PORT=8080
JWT_SECRET=replace-with-a-long-random-secret- Deploy the service.
- Add a public domain from the Railway service settings.
Railway already provides HTTPS at the edge, so ENABLE_HTTPS is normally not needed there.
π Other hosting options
Lynx can run anywhere that supports a Node app or a Docker container: Render, Fly.io, DigitalOcean App Platform, Google Cloud Run, Heroku Container Runtime, Azure App Service, AWS Elastic Beanstalk, Koyeb, Northflank, CapRover, Dokku, and Coolify.
For any container deployment, persist /app/data and set JWT_SECRET.
v4.0.0
- Redesigned the Admin panel with a clearer dashboard layout, status metrics, sticky centered navigation, and a lighter operational workspace.
- Improved the Links editor with a clearer toolbar, content creation cards, save state visibility, and a more helpful empty state.
- Added animated profile checklist guidance and save confirmation feedback for theme changes.
- Kept the public page preview isolated from the Admin styling so it continues to reflect the saved public theme.
- Added a single public-page payload endpoint to load profile, links, and theme together and avoid flashes of default content.
- Preserved compatibility with existing SQLite databases through additive migrations only.
v3.8.0
- Added Google Analytics 4 integration in the Admin panel (new Integrations tab).
- The GA4 Measurement ID (
G-XXXXXXXXXX) is stored in the database and injected as agtag.jsscript on the public page only β the admin panel is never tracked. - Content Security Policy updated to allow
googletagmanager.comandgoogle-analytics.comscript and connect sources. - Measurement ID is validated client-side before saving (format
G-XXXXXXXXXX).
v3.7.0
- Fixed production blank page behavior caused by CORS/CSP headers blocking API calls in production containers.
- Fixed stale frontend assets in Docker builds by cleaning
distbefore building. - Fixed legacy database migration handling.
- Fixed missing
fsimport indatabase.js.
- Improved production CORS handling for same-origin and reverse-proxy deployments.
- Refined Content Security Policy settings.
- Added static asset serving logs for deployment troubleshooting.
- Improved database migration validation and error handling.
v3.6.0
- Added live preview inside the admin panel.
- Added a View Public Page action from the admin header.
- Added link visibility toggles.
- Added mobile drag-and-drop ordering.
- Removed sensitive authentication logs.
- Removed unused Supabase and Firebase code.
- Fixed duplicate database migration logic.
- Removed debug logging from
PublicLinkCard.
v3.5.1
- Updated vulnerable frontend, backend, and Docker dependencies.
- Resolved Dependabot alerts and Docker image CVEs reported at the time of release.
- Optimized Docker build time by avoiding source builds where precompiled binaries are available.
v3.5.0
- Added editable profile fields with line-break support in the bio.
- Added social link controls and profile picture display controls.
- Added text cards and bulleted lists.
- Added JSON import/export for links and themes.
- Added theme controls for page styling, typography, title, meta description, and footer text.
- Added Docker startup validation for
JWT_SECRET. - Added optional self-signed HTTPS support with
ENABLE_HTTPS=true.
MIT License. See LICENSE.txt.

