Troubleshooting
This page covers common issues you may encounter when self-hosting lnkify, along with diagnostic steps and solutions.
1. TLS Certificate Failed
Symptoms: Caddy logs show TLS errors, browsers show certificate warnings, the site is inaccessible over HTTPS.
Causes:
- Ports 80 and/or 443 are not reachable from the internet.
- DNS records have not propagated.
- A firewall is blocking inbound traffic.
Diagnosis:
# Check if ports are listening
docker compose ps caddy
# Check Caddy logs for ACME errors
docker compose logs caddy | grep -i "acme\|tls\|error"
# Verify DNS points to your server
dig lnkify.io +short
# Test port reachability from an external host
curl -I http://lnkify.ioSolutions:
- Ensure ports 80 and 443 are open on the host firewall and any network firewalls (cloud provider security groups).
- Verify DNS A records point to the correct IP. Wait for DNS propagation if records were recently changed.
- Restart Caddy after DNS is confirmed:
docker compose restart caddy. - If you've requested many certificates recently, Let's Encrypt rate limits may apply. Wait an hour and retry.
2. Server Can't Connect to Database
Symptoms: Server logs show ECONNREFUSED, Connection refused, or Prisma connection errors. Server restarts repeatedly.
Causes:
- The
dbservice is not healthy. DATABASE_URLis misconfigured.- The server started before the database healthcheck passed (unlikely with
condition: service_healthy, but possible if the healthcheck is misconfigured).
Diagnosis:
# Check database health
docker compose ps db
# Check database logs
docker compose logs db
# Test connectivity from server
docker compose exec server sh -c "nc -zv db 5432"
# Verify DATABASE_URL
docker compose exec server sh -c "echo \$DATABASE_URL"Solutions:
- Wait for the database to become healthy. The server depends on
condition: service_healthy, so it won't start untilpg_isreadypasses. - Verify
DATABASE_URLmatches the database credentials in.env:The host must beDATABASE_URL=postgresql://postgres:password@db:5432/lnkifydb(the Docker Compose service name), notlocalhost. - Check that
POSTGRES_USER,POSTGRES_PASSWORD, andPOSTGRES_DBare set correctly in.env.
3. 401 Unauthorized on MCP
Symptoms: MCP clients receive 401 Unauthorized when connecting to https://mcp.lnkify.io.
Causes:
- Missing
x-api-keyheader in MCP requests. - The API key format is incorrect.
- The API key has been revoked.
Diagnosis:
# Test MCP endpoint with a valid API key
curl -H "x-api-key: lf_live_abc123..." https://mcp.lnkify.io/mcp
# Check server logs for auth failures
docker compose logs server | grep "401\|unauthorized\|api.key"Solutions:
- Ensure your MCP client is configured to send the
x-api-keyheader. See MCP Authentication. - Verify the API key is active in the lnkify dashboard under API Keys.
- Create a new API key if the existing one has been revoked.
- The key format should be
lf_live_<random_string>.
4. Rate Limited (429)
Symptoms: Requests return 429 Too Many Requests with RateLimit-* response headers.
Causes:
- You've exceeded the configured rate limits.
- Rate limits are too restrictive for your use case.
Diagnosis:
# Check response headers for rate limit info
curl -vI https://lnkify.io/graphql
# Look for headers: RateLimit-Limit, RateLimit-Remaining, RateLimit-ResetSolutions:
- Wait for the rate limit window to reset (typically 1 minute).
- Increase rate limits in
.env:envRATELIMIT_GRAPHQL_PER_MIN=300 RATELIMIT_REDIRECT_PER_MIN=1200 RATELIMIT_BULK_PER_MIN=30 - Restart the server to reset all in-memory rate limit counters:
docker compose restart server. - Rate limit counters are per-identity (for GraphQL) or per-IP (for redirects). Ensure
TRUST_PROXY=1(the single Caddy hop) so real client IPs are used — never set it totrue, which trusts the entire client-suppliedX-Forwarded-Forchain.
5. Container Keeps Restarting
Symptoms: docker compose ps shows a service continuously restarting.
Causes:
- Missing required environment variables.
- A configuration file is invalid.
- The service crashes immediately on startup.
Diagnosis:
# Watch logs for the failing service
docker compose logs -f server
# Check restart count
docker compose ps -a
# Inspect the container exit code
docker inspect lnkify_server_1 | grep -A5 StateSolutions:
- Check for missing required variables:
JWT_SECRET,DATABASE_URL,APIKEY_PEPPERare mandatory. - Verify the
.envfile is present and not empty. - For the Caddy service, check the Caddyfile syntax:
docker compose run --rm caddy caddy validate --config /etc/caddy/Caddyfile. - For the server, run it interactively to see startup errors:bash
docker compose run --rm server
6. Shortlinks Not Redirecting
Symptoms: Visiting a shortlink (e.g., https://lnkify.io/abc123) does not redirect. You see a 404 or the frontend page.
Causes:
- The slug does not exist in the database.
- Caddy is routing the request to the frontend instead of the server.
- The server is not running.
Diagnosis:
# Test the redirect directly against the server
docker compose exec server sh -c "curl -v http://localhost:4000/abc123"
# Check Caddy routing — verify the Caddyfile catch-all is present
docker compose exec caddy cat /etc/caddy/CaddyfileSolutions:
- Verify the slug exists in the database:bash
docker compose exec db psql -U postgres -d lnkify -c "SELECT id, lnkify, target FROM \"Lnkify\" WHERE lnkify = 'abc123';" - Ensure the Caddyfile catch-all
handle { reverse_proxy server:4000 }is present at the end of thelnkify.ioblock. - Check that the server is running and healthy:
docker compose ps server.
7. Analytics Not Showing Location Data
Symptoms: Click analytics show no city/country data. All clicks appear without geographic information.
Causes:
IPREGISTRY_API_KEYis not set.- The IP registry API key is invalid or has expired.
- The ipregistry.co service is unreachable.
Diagnosis:
# Check if the variable is set
docker compose exec server sh -c "echo \$IPREGISTRY_API_KEY"
# Test the API key manually
curl "https://api.ipregistry.co/8.8.8.8?key=$(docker compose exec -T server sh -c 'echo $IPREGISTRY_API_KEY')"Solutions:
- Get a free API key from ipregistry.co.
- Add it to
.env:IPREGISTRY_API_KEY=ir_... - Restart the server:
docker compose restart server. - Note: Location data is only available for new clicks after the key is set.
8. MCP Sessions Dropping
Symptoms: MCP clients disconnect intermittently and must reconnect.
Causes:
- Server restart clears the in-memory session map.
- Single-replica limitation — sessions are not shared.
Solutions:
- This is expected behavior for the single-replica architecture. See Scaling for details.
- If using multiple replicas, configure sticky sessions in Caddy (see Scaling).
- MCP clients should implement reconnection logic. The session drop is brief and clients can reconnect after restart.
Useful Diagnostic Commands
# Service status
docker compose ps
# Follow all logs
docker compose logs -f
# Follow a specific service's logs
docker compose logs -f server
docker compose logs -f caddy
docker compose logs -f db
# Enter a service container
docker compose exec server sh
docker compose exec db sh
# Check if .env is loaded correctly
docker compose config
# Check resource usage
docker stats
# Restart all services
docker compose restart
# Full restart (stop, recreate, start)
docker compose up -d --force-recreatePort Conflicts
If ports 80 or 443 are already in use on the host:
# Check what's using port 80
sudo lsof -i :80
# Check what's using port 443
sudo lsof -i :443Common culprits: nginx, Apache, another Caddy instance, or a previous lnkify installation. Stop any conflicting services before starting lnkify.
Getting Help
If you encounter an issue not covered here:
- Search existing issues: GitHub Issues
- Check discussions: GitHub Discussions
- Open a new issue: Include
docker compose logs,.env(with secrets redacted), and steps to reproduce.
Next: Architecture