#!/bin/sh set -e echo "==> VpnPanel starting" # Always build DATABASE_URL from POSTGRES_* (Dokploy-friendly) export DATABASE_URL="$( python - <<'PY' import os from urllib.parse import quote_plus user = os.environ.get("POSTGRES_USER", "vpn") password = os.environ.get("POSTGRES_PASSWORD", "vpn") db = os.environ.get("POSTGRES_DB", "vpn_panel") print(f"postgresql+asyncpg://{quote_plus(user)}:{quote_plus(password)}@postgres:5432/{db}") PY )" echo "Waiting for database..." python - <<'PY' import asyncio import os import sys from sqlalchemy import text from sqlalchemy.ext.asyncio import create_async_engine async def wait_db(retries: int = 60) -> None: url = os.environ["DATABASE_URL"] engine = create_async_engine(url) for i in range(retries): try: async with engine.connect() as conn: await conn.execute(text("SELECT 1")) await engine.dispose() print("Database is ready") return except Exception as exc: # noqa: BLE001 print(f"DB not ready ({i + 1}/{retries}): {exc}") await asyncio.sleep(1) await engine.dispose() print("ERROR: database did not become ready", file=sys.stderr) sys.exit(1) asyncio.run(wait_db()) PY echo "Running migrations..." alembic upgrade head echo "Bootstrapping admin..." python -m app.bootstrap echo "Starting uvicorn..." exec "$@"