# 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