nusaas@self-host:~$ [back-to-home]
[docker-hub]
> diagnose --verbose status.log

Self-Host Troubleshooting Guide

A comprehensive manual mapping root causes, immediate hot fixes, and permanent remedies for common infrastructure issues encountered during self-hosting.

CORS-01

CORS Policy / Preflight Request Blocked

[Severity: High]

// SYMPTOM

Access to fetch at 'https://api.yourdomain.com/api/auth/login' from origin 'https://app.yourdomain.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present.

// ROOT_CAUSE

The frontend origin does not match CORS_ALLOWED_ORIGIN_PATTERNS, or the regex begins with an unquoted #. Dotenv treats an unquoted leading hash as a comment, leaving Laravel with no allowed browser origin.

// IMMEDIATE_HOT_FIX

Set a quoted exact regex for your frontend origin, then recreate every backend runtime so it loads the changed environment:

# In /opt/nusaas/.env:
CORS_ALLOWED_ORIGIN_PATTERNS='#^https://app\.yourdomain\.com$#'

docker compose up -d --force-recreate api worker cron ws
CACHE-02

Browser Connects to Stale/Old Domains

[Severity: Medium]

// SYMPTOM

You updated domain settings in web/.env.frontend, but the browser still targets old API or WebSocket domains.

// ROOT_CAUSE

1. Running docker compose restart only restarts container processes; it does not recreate them with new environment variables.
2. The React Frontend uses a Service Worker (sw.js) that aggressively caches JS files locally in the client browser, forcing it to keep loading the old config.

// SOLUTION_STEPS

  1. Force recreate the containers to inject the new env values:
    docker compose up -d --force-recreate web
  2. Test in an Incognito / Private Window (bypasses Service Worker cache).
  3. In your main window, open DevTools (F12), navigate to the Network tab, check Disable Cache, and reload with Ctrl+F5 (Windows) or Cmd+Shift+R (Mac).
  4. If using Firefox, unregister the service worker directly at: about:debugging#/runtime/this-firefox
SSL-03

Cloudflare ERR_SSL_VERSION_OR_CIPHER_MISMATCH

[Severity: High]

// SYMPTOM

This site can’t provide a secure connection. app.erp.yourdomain.com uses an unsupported protocol.
ERR_SSL_VERSION_OR_CIPHER_MISMATCH

// ROOT_CAUSE

Cloudflare's free Universal SSL certificate only covers wildcards up to the 3rd level (*.yourdomain.com). If you deploy using nested 4th-level subdomains (like app.erp.yourdomain.com), Cloudflare cannot negotiate a secure SSL handshake at the Edge unless you purchase their Advanced Certificate Manager (ACM).

// REMEDY

Flatten your subdomain structure to the 3rd level. Remove the nested erp. segment entirely:

  • Use app.yourdomain.com or app-erp.yourdomain.com
  • Use api.yourdomain.com or api-erp.yourdomain.com
DB-04

Database Migration & Seeding Hangs

[Severity: Medium]

// SYMPTOM

The installation script gets stuck indefinitely at:
[NuSaaS] Running database migrations and seeding... [/]

// ROOT_CAUSE

When running migrations, Laravel shells out to the native mysql binary inside the backend container to load the schema dump file. If your backend container's default MySQL client (Debian's MariaDB client) connects to a MySQL 8.0 server advertising self-signed SSL certs, the handshake fails/hangs with ERROR 2026 (HY000): TLS/SSL error.

// IMMEDIATE_HOT_FIX

Current backend images configure the container client correctly. Pull and recreate the API before retrying migrations:

docker compose pull api
docker compose up -d --force-recreate api
docker compose exec -T api php artisan migrate --seed --force
TLS-05

Automatic HTTPS Certificate Fails

[Severity: Medium]

// SYMPTOM

The proxy runs, but the app or API hostname has no valid certificate.

// ROOT_CAUSE

DNS does not point to this host, a cloud or host firewall blocks 80/443, or another service owns those ports. On WSL2, the conflicting listener may be a Windows process.

// DIAGNOSIS

docker compose ps
docker compose logs --since=10m proxy
ss -ltn '( sport = :80 or sport = :443 )'
# Replace with your public API hostname:
curl -I https://api.example.com/api/health
DIAG-07

NuSaaS Diagnostics Suite

[Info & Utilities]

// 1. CHECKING_CONTAINER_HEALTH

Query container statuses, check if their internal health checks are passing, and ping the API route directly from the server:

# Get docker containers status & health indicators
docker compose ps

# Check the API health endpoint directly inside the API container
docker compose exec api curl -sf http://localhost:4000/api/health

# Verify actual HTTP response headers from the API server
docker compose exec api curl -i http://localhost:4000/api/health

// 2. LIVE_LOG_STREAMING

Stream logs in real-time to intercept errors during database connection failures, mail queue issues, or application crashes:

# Stream logs from all running containers
docker compose logs -f

# Stream only backend API container logs
docker compose logs -f api

# Stream queue worker logs
docker compose logs -f worker

# Stream MySQL logs
docker compose logs -f db

// 3. CONTAINER_SHELLS_AND_DATABASE_DIAGNOSTICS

Access container environments directly or trigger internal database pingers to verify connection integrity:

# Open an interactive shell inside the API container
docker compose exec -it api bash

# Monitor DB connection stats in Laravel
docker compose exec api php artisan db:monitor

# Directly test DB connection PDO instantiation
docker compose exec api php artisan tinker --execute="DB::connection()->getPdo();"

# Get current system stats (RAM/CPU usage per container)
docker stats --no-stream