# Deploying SpomeniQR on Coolify This guide covers deploying SpomeniQR to a VPS using [Coolify](https://coolify.io/) — a self-hosting platform similar to Heroku/Vercel that manages Docker deployments, SSL, and databases. ## Prerequisites - A VPS with Docker installed (Coolify handles this) - Coolify installed on your VPS (see [coolify.io/docs](https://coolify.io/docs/installation)) - Domain `testbed.mk` with DNS configured: - **A record** `@` → your VPS IP - **A record** `*` → your VPS IP (wildcard for subdomains) - A Contabo S3 bucket set up with public read policy ## Architecture Overview Coolify will run two containers: 1. **app** — Next.js standalone build (Dockerfile) 2. **db** — PostgreSQL 16 (Coolify managed database) We do **not** deploy Nginx via Docker — Coolify has its own reverse proxy (Traefik/Caddy) that handles SSL, subdomain routing, and the `X-Subdomain` header. ``` Internet │ ├── *.testbed.mk ──► Coolify Proxy (:80/:443) │ ├── Extracts subdomain → sets X-Subdomain header │ └── Proxies to app:3000 │ └── testbed.mk ──► Coolify Proxy ──► app:3000 ``` ## Step 1: Add a New Project in Coolify 1. Open your Coolify dashboard 2. Click **+ Add New Project** 3. Name it `SpomeniQR` ## Step 2: Create the Database 1. Inside the project, click **+ Add New Resource** → **Database** 2. Select **PostgreSQL** 3. Configure: - **Name:** `spomeniqr-db` - **PostgreSQL Version:** 16 - **Database Name:** `monuments` - **Username:** `postgres` - **Password:** generate a strong password or set your own 4. Click **Deploy** 5. After deployment, note the **Internal Connection String** — it looks like: ``` postgresql://postgres:YOUR_PASSWORD@spomeniqr-db:5432/monuments ``` You'll need this for `DATABASE_URL`. ## Step 3: Add the Application 1. Inside the project, click **+ Add New Resource** → **Application** 2. Select **Public Repository** (or **Private** if your repo is private) 3. Configure: - **Name:** `spomeniqr` - **Repository URL:** your Git repo URL - **Branch:** `main` - **Build Pack:** **Nixpacks** (auto-detected) or **Docker** (uses the Dockerfile) ### Option A: Nixpacks (Recommended — simpler) Leave the build pack as **Nixpacks**. Coolify will auto-detect Next.js and build it. Add these **Build Commands:** ``` npx prisma generate && npm run build ``` Add this **Start Command:** ``` npx prisma migrate deploy && node .next/standalone/server.js ``` ### Option B: Docker (uses the project's Dockerfile) Set **Build Pack** to **Docker**. Coolify will use the `Dockerfile` in the repo root. No additional configuration needed — it already includes Prisma migration and standalone server startup. **Note:** If using Docker build pack, the `DATABASE_URL` must use `spomeniqr-db` as the host (Coolify internal network), not `localhost`. ## Step 4: Configure Environment Variables In the application settings, go to **Environment Variables** and add: ```env # Database — use the Coolify internal connection string DATABASE_URL=postgresql://postgres:YOUR_PASSWORD@spomeniqr-db:5432/monuments # Clerk NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_live_... CLERK_SECRET_KEY=sk_live_... NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up NEXT_PUBLIC_CLERK_SIGN_IN_FALLBACK_REDIRECT_URL=/ NEXT_PUBLIC_CLERK_SIGN_UP_FALLBACK_REDIRECT_URL=/ # Contabo S3 S3_ENDPOINT=https://eu2.contabostorage.com S3_REGION=eu-2 S3_ACCESS_KEY_ID=your-access-key S3_SECRET_ACCESS_KEY=your-secret-key S3_BUCKET_NAME=monuments-images # App NEXT_PUBLIC_APP_URL=https://testbed.mk NEXT_PUBLIC_APP_DOMAIN=testbed.mk # Node NODE_ENV=production ``` **Important:** - `DATABASE_URL` must point to the Coolify **internal** hostname (`spomeniqr-db`), not `localhost`. - Use your **production** Clerk keys (`pk_live_` / `sk_live_`), not the test ones. ## Step 5: Configure Domain & Subdomain Routing ### Set the Main Domain 1. In the application settings, go to **Configuration** → **Domains** 2. Add: `testbed.mk` 3. Enable **HTTPS** — Coolify will auto-provision a Let's Encrypt certificate ### Enable Wildcard Subdomain Routing This is the critical part. Coolify's proxy needs to: 1. Accept requests for `*.testbed.mk` 2. Extract the subdomain and pass it as the `X-Subdomain` header to the Next.js app #### Option A: Coolify Proxy Configuration (Recommended) 1. In your application, go to **Configuration** → **Domains** 2. Add both domains: - `testbed.mk` - `*.testbed.mk` 3. Coolify will request a wildcard SSL certificate. If your DNS provider supports DNS-01 challenges (Cloudflare, Route53, etc.), this works automatically. Otherwise, you may need to add each subdomain manually. #### Option B: Custom Proxy Configuration If Coolify doesn't support wildcard domains easily, add a **custom Caddy/Traefik configuration** in the Coolify settings: For **Caddy** (Coolify's default proxy): Create a file at `/data/coolify/proxy/caddy/custom/testbed.mk`: ``` *.testbed.mk { reverse_proxy app:3000 { header_up X-Subdomain {http.request.host.labels.2} header_up X-Forwarded-Proto {scheme} } } testbed.mk { reverse_proxy app:3000 { header_up X-Forwarded-Proto {scheme} } } ``` For **Traefik** (alternative proxy), you'd add labels to the container: ```yaml traefik.http.routers.spomeniqr.rule: HostRegexp(`{subdomain:[a-z0-9-]+}.testbed.mk`) || Host(`testbed.mk`) traefik.http.middlewares.spomeniqr-subdomain.headers.customrequestheaders.X-Subdomain: ``` > **Note:** The proxy configuration varies based on your Coolify version and proxy choice. Check the Coolify docs for the latest instructions on wildcard domains. ## Step 6: Configure Clerk 1. Go to [Clerk Dashboard](https://dashboard.clerk.com) 2. Switch to your **Production** instance 3. Under **Paths**, set: - Sign-in: `/sign-in` - Sign-up: `/sign-up` 4. Under **Domains**, add: - `testbed.mk` - `*.testbed.mk` ## Step 7: Configure Contabo S3 1. Log into Contabo Object Storage 2. Create a bucket named `monuments-images` in region `eu-2` 3. Set the **bucket policy** for public read: ```json { "Version": "2012-10-17", "Statement": [ { "Sid": "PublicReadGetObject", "Effect": "Allow", "Principal": { "AWS": ["*"] }, "Action": ["s3:GetObject"], "Resource": ["arn:aws:s3:::monuments-images/*"] } ] } ``` 4. Set **CORS** to allow uploads: ```json [ { "AllowedHeaders": ["*"], "AllowedMethods": ["GET", "PUT", "POST"], "AllowedOrigins": ["https://testbed.mk", "https://*.testbed.mk"], "ExposeHeaders": ["ETag"], "MaxAgeSeconds": 3600 } ] ``` 5. Create an API key with read/write permissions ## Step 8: Deploy 1. Click **Deploy** in the Coolify dashboard 2. Watch the build logs — it should: - Install dependencies - Run `npx prisma generate` - Build the Next.js app - Run `npx prisma migrate deploy` - Start the server on port 3000 3. Once deployed, visit `https://testbed.mk` to verify ## Step 9: Verify Subdomain Routing Test that wildcard subdomains work: 1. Create a memorial page with subdomain `test-memorial` 2. Visit `https://test-memorial.testbed.mk` 3. Check browser DevTools network tab — the `X-Subdomain` header should be set by the proxy 4. The Next.js middleware reads `X-Subdomain` and rewrites to the correct page If subdomains aren't working: - Check DNS: `dig *.testbed.mk +short` should return your VPS IP - Check Coolify proxy logs for the wildcard domain configuration - Verify the `X-Subdomain` header is being set in the proxy config ## Step 10: Verify Everything ```bash # App health curl -s https://testbed.mk | head -5 # Subdomain routing curl -sI https://test-memorial.testbed.mk | head -5 # API health curl -s https://testbed.mk/api/check-subdomain?slug=test | python3 -m json.tool # Database connection (from Coolify terminal) docker exec spomeniqr-app npx prisma db push --accept-data-loss ``` ## Troubleshooting ### Build fails with Prisma errors Make sure `DATABASE_URL` points to the Coolify internal hostname (e.g., `spomeniqr-db:5432`), not `localhost`. The app and database must be on the same Coolify network. ### Images return 401 from S3 Make sure you've applied the bucket policy for public read. See Step 7. If Contabo doesn't serve public objects via URL, the app uses an `/api/image?key=...` proxy route as a fallback. ### Subdomain routing not working - Verify DNS wildcard `*.testbed.mk` points to your VPS - Check that Coolify's proxy config includes both `testbed.mk` and `*.testbed.mk` - Check the proxy access logs — the `X-Subdomain` header should appear - If using a custom Caddyfile, make sure it's in the right directory and reload the proxy ### Clerk authentication issues - Ensure production Clerk keys are set (not `pk_test_` / `sk_test_`) - Verify `testbed.mk` and `*.testbed.mk` are added in Clerk dashboard domains - Check that `NEXT_PUBLIC_APP_URL` and `NEXT_PUBLIC_APP_DOMAIN` are set correctly ### Database migration issues If migrations fail on deploy, you can run them manually from the Coolify terminal: ```bash # SSH into the app container docker exec -it spomeniqr-app sh # Run migrations npx prisma migrate deploy # Or push schema changes directly npx prisma db push ``` ## Environment Variables Reference | Variable | Required | Description | |----------|----------|-------------| | `DATABASE_URL` | Yes | PostgreSQL connection string (Coolify internal) | | `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY` | Yes | Clerk publishable key (`pk_live_...`) | | `CLERK_SECRET_KEY` | Yes | Clerk secret key (`sk_live_...`) | | `NEXT_PUBLIC_CLERK_SIGN_IN_URL` | Yes | `/sign-in` | | `NEXT_PUBLIC_CLERK_SIGN_UP_URL` | Yes | `/sign-up` | | `S3_ENDPOINT` | Yes | Contabo S3 endpoint URL | | `S3_REGION` | Yes | Contabo S3 region (e.g. `eu-2`) | | `S3_ACCESS_KEY_ID` | Yes | S3 access key | | `S3_SECRET_ACCESS_KEY` | Yes | S3 secret key | | `S3_BUCKET_NAME` | Yes | S3 bucket name | | `NEXT_PUBLIC_APP_URL` | Yes | `https://testbed.mk` | | `NEXT_PUBLIC_APP_DOMAIN` | Yes | `testbed.mk` | | `NODE_ENV` | Yes | `production` | ## Useful Coolify Commands - **Redeploy:** Project → Application → Deploy - **View Logs:** Project → Application → Logs - **SSH into container:** Project → Application → Terminal - **Database Management:** Project → Database → Admin (pgAdmin or Prisma Studio) - **SSL Certificates:** Managed automatically by Coolify for configured domains ## Updates To update the application: 1. Push changes to your Git repository 2. Coolify will auto-deploy if **Auto Deploy** is enabled, or click **Deploy** manually 3. The `start.sh` script runs `npx prisma migrate deploy` on every startup, so schema changes are applied automatically