# Placebo.mk Deployment Guide (Coolify) Complete deployment guide for the Placebo.mk satirical news platform on a VPS using Coolify. ## Table of Contents 1. [Architecture Overview](#architecture-overview) 2. [Prerequisites](#prerequisites) 3. [Coolify Setup](#coolify-setup) 4. [Database Configuration](#database-configuration) 5. [Service Deployments](#service-deployments) 6. [Environment Variables Reference](#environment-variables-reference) 7. [Domain & SSL Configuration](#domain--ssl-configuration) 8. [Push Notifications Setup](#push-notifications-setup) 9. [Post-Deployment Checklist](#post-deployment-checklist) 10. [Troubleshooting](#troubleshooting) --- ## Architecture Overview ``` ┌──────────────────────────────────────┐ │ Coolify Reverse Proxy │ │ (Traefik/Caddy) │ └────────────────┬─────────────────────┘ │ ┌────────────────────────────────┼────────────────────────────────┐ │ │ │ ▼ ▼ ▼ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ Frontend │ │ PWA │ │ CMS │ │ (React+Vite) │ │ (React+Vite+SW) │ │ (Strapi) │ │ nginx:80 │ │ nginx:80 │ │ Node.js:1337 │ │ placebo.mk │ │ app.placebo.mk │ │ cms.placebo.mk │ └────────┬────────┘ └────────┬────────┘ └────────┬────────┘ │ │ │ │ │ │ │ │ │ └────────────────────────────────┼────────────────────────────────┘ │ ▼ ┌─────────────────┐ │ Backend │ │ (NestJS) │ │ Node.js:3000 │ │ api.placebo.mk │ └────────┬────────┘ │ ▼ ┌─────────────────┐ │ PostgreSQL │ │ :5432 │ └─────────────────┘ ``` ### Tech Stack | Service | Technology | Port | Purpose | |---------|------------|------|---------| | Frontend | React 19 + Vite + Tailwind | 80 | Main website (SSG) | | PWA | React 19 + Vite + Workbox | 80 | Progressive Web App with push | | Backend | NestJS + TypeORM | 3000 | REST API | | CMS | Strapi 5 | 1337 | Content management | | Database | PostgreSQL 16 | 5432 | Primary data store | --- ## Prerequisites ### Server Requirements | Resource | Minimum | Recommended | |----------|---------|-------------| | RAM | 4GB | 8GB | | CPU | 2 vCPU | 4 vCPU | | Storage | 40GB SSD | 80GB SSD | | OS | Ubuntu 22.04 | Ubuntu 24.04 | ### Domain Setup Configure these DNS A records pointing to your VPS IP: | Type | Name | Purpose | |------|------|---------| | A | `@` | Main website (frontend) | | A | `www` | WWW redirect | | A | `api` | Backend API | | A | `app` | PWA | | A | `cms` | Strapi admin | ### Required Before Deployment - [ ] Coolify installed and accessible - [ ] Domain DNS configured - [ ] GitHub repository connected to Coolify - [ ] SMTP server (optional, for emails) --- ## Coolify Setup ### 1. Create Project 1. Login to Coolify dashboard 2. Navigate to **Projects** 3. Click **+ New Project** 4. Name: `placebo` 5. Click **Create** ### 2. Create Environment 1. Inside `placebo` project 2. Click **+ New Environment** 3. Name: `production` 4. Click **Create** ### 3. Connect Repository 1. Go to **Configuration** → **Source** 2. Click **Connect GitHub** 3. Authorize Coolify to access your repositories 4. Select repository: `your-org/placeboMk` --- ## Database Configuration ### Create PostgreSQL Service 1. In `production` environment, click **+ New Resource** 2. Select **Database** → **PostgreSQL** 3. Configure: | Setting | Value | |---------|-------| | Name | `placebo-db` | | Version | `16-alpine` | | PostgreSQL User | `placebo_user` | | PostgreSQL Password | `` | | PostgreSQL Database | `placebo_db` | | Persistent Volume | Enabled | 4. Click **Deploy** ### Note Connection Details After deployment, note the internal hostname: ``` Host: placebo-db Port: 5432 Database: placebo_db Username: placebo_user Password: ``` --- ## Service Deployments ### 1. Backend (NestJS API) #### Create Service 1. Click **+ New Resource** → **Service** 2. Select **Dockerfile** (not Docker Compose) 3. Name: `placebo-backend` 4. Repository: Select your connected repo #### Configuration | Setting | Value | |---------|-------| | Dockerfile Location | `backend/Dockerfile` | | Build Context | `backend` | | Port | `3000` | #### Environment Variables ```env NODE_ENV=production PORT=3000 # Database DATABASE_TYPE=postgres DATABASE_HOST=placebo-db DATABASE_PORT=5432 DATABASE_USERNAME=placebo_user DATABASE_PASSWORD= DATABASE_NAME=placebo_db DATABASE_SYNCHRONIZE=false DATABASE_LOGGING=false # JWT Authentication JWT_SECRET= JWT_EXPIRATION=86400 # CORS CORS_ORIGIN=https://placebo.mk,https://www.placebo.mk,https://app.placebo.mk # Strapi CMS STRAPI_URL=https://cms.placebo.mk STRAPI_API_TOKEN= # Push Notifications VAPID_SUBJECT=mailto:contact@placebo.mk VAPID_PUBLIC_KEY= VAPID_PRIVATE_KEY= ``` #### Generate Secrets **JWT Secret:** ```bash openssl rand -base64 64 | tr -d '\n' ``` **VAPID Keys** (run locally): ```bash cd backend npm install npm run generate-vapid ``` #### Health Check | Setting | Value | |---------|-------| | Path | `/health` | | Interval | 30s | | Timeout | 10s | | Retries | 3 | #### Domain 1. Go to **Domains** tab 2. Add: `api.placebo.mk` 3. Enable **HTTPS** (Let's Encrypt) --- ### 2. Frontend (Main Website) #### Create Service 1. Click **+ New Resource** → **Service** → **Dockerfile** 2. Name: `placebo-frontend` #### Configuration | Setting | Value | |---------|-------| | Dockerfile Location | `frontend/Dockerfile` | | Build Context | `frontend` | | Port | `80` | #### Build Arguments Vite requires environment variables at build time. Add these as **Build Args**: ```env VITE_API_URL=https://api.placebo.mk/api/v1 VITE_CMS_URL=https://cms.placebo.mk ``` #### Domain 1. Add: `placebo.mk` 2. Add: `www.placebo.mk` 3. Enable **HTTPS** --- ### 3. PWA (Progressive Web App) #### Create Service 1. Click **+ New Resource** → **Service** → **Dockerfile** 2. Name: `placebo-pwa` #### Configuration | Setting | Value | |---------|-------| | Dockerfile Location | `pwa/Dockerfile` | | Build Context | `pwa` | | Port | `80` | #### Build Arguments ```env VITE_API_URL=https://api.placebo.mk/api/v1 VITE_CMS_URL=https://cms.placebo.mk ``` #### Persistent Volume (Optional) For service worker and cache: - Container Path: `/usr/share/nginx/html` - Host Path: `pwa-dist` #### Domain 1. Add: `app.placebo.mk` 2. Enable **HTTPS** --- ### 4. CMS (Strapi) #### Create Service 1. Click **+ New Resource** → **Service** → **Dockerfile** 2. Name: `placebo-cms` #### Configuration | Setting | Value | |---------|-------| | Dockerfile Location | `cms/cms/Dockerfile` | | Build Context | `cms/cms` | | Port | `1337` | #### Environment Variables ```env NODE_ENV=production HOST=0.0.0.0 PORT=1337 # Database DATABASE_CLIENT=postgres DATABASE_HOST=placebo-db DATABASE_PORT=5432 DATABASE_NAME=placebo_db DATABASE_USERNAME=placebo_user DATABASE_PASSWORD= DATABASE_SSL=false # Security Keys (generate unique values for each) APP_KEYS= API_TOKEN_SALT=<32-char-secret> ADMIN_JWT_SECRET=<32-char-secret> TRANSFER_TOKEN_SALT=<32-char-secret> JWT_SECRET=<32-char-secret> # CORS CORS_ORIGIN=https://placebo.mk,https://app.placebo.mk,https://api.placebo.mk ``` #### Generate Strapi Keys **For APP_KEYS** (4 comma-separated keys): ```bash node -e "console.log(require('crypto').randomBytes(32).toString('base64'))" # Run 4 times, join with commas ``` **For other secrets:** ```bash openssl rand -base64 32 ``` #### Persistent Volumes Add these volumes for data persistence: | Container Path | Purpose | |----------------|---------| | `/app/public/uploads` | Media uploads | | `/app/.tmp` | Temporary files | #### Domain 1. Add: `cms.placebo.mk` 2. Enable **HTTPS** --- ## Environment Variables Reference ### Backend | Variable | Required | Description | |----------|----------|-------------| | `NODE_ENV` | Yes | Set to `production` | | `DATABASE_HOST` | Yes | PostgreSQL hostname | | `DATABASE_PORT` | Yes | PostgreSQL port (5432) | | `DATABASE_USERNAME` | Yes | Database username | | `DATABASE_PASSWORD` | Yes | Database password | | `DATABASE_NAME` | Yes | Database name | | `DATABASE_SYNCHRONIZE` | Yes | Set to `false` in production | | `JWT_SECRET` | Yes | 64+ character secret | | `CORS_ORIGIN` | Yes | Comma-separated allowed origins | | `VAPID_PUBLIC_KEY` | Yes | For push notifications | | `VAPID_PRIVATE_KEY` | Yes | For push notifications | | `STRAPI_URL` | Yes | CMS URL | | `STRAPI_API_TOKEN` | Yes | API token from Strapi | ### Frontend/PWA (Build Args) | Variable | Required | Description | |----------|----------|-------------| | `VITE_API_URL` | Yes | Backend API URL | | `VITE_CMS_URL` | Yes | CMS URL | ### CMS (Strapi) | Variable | Required | Description | |----------|----------|-------------| | `APP_KEYS` | Yes | 4 comma-separated 32-byte keys | | `API_TOKEN_SALT` | Yes | Random 32-byte string | | `ADMIN_JWT_SECRET` | Yes | Random 32-byte string | | `JWT_SECRET` | Yes | Random 32-byte string | | `TRANSFER_TOKEN_SALT` | Yes | Random 32-byte string | --- ## Domain & SSL Configuration ### Configure Each Service For each deployed service: 1. Go to service → **Configuration** → **Domains** 2. Click **Add Domain** 3. Enter domain (e.g., `api.placebo.mk`) 4. Enable **HTTPS** 5. Select **Let's Encrypt** 6. Click **Request Certificate** ### SSL Certificate Generation Coolify automatically handles SSL via Let's Encrypt. Ensure: - DNS is properly configured - Port 80 and 443 are open - Domain is accessible from internet --- ## Push Notifications Setup ### 1. Generate VAPID Keys Run locally in the backend directory: ```bash cd backend npm install npm run generate-vapid ``` Output: ``` Generating VAPID keys... VAPID keys generated and added to .env file: Public Key: BIAna-ulT88Ek5ZdiAiXJikks3T5JEuZjBstuO7bEbUJ6RAoSmXAbum1Pf7JIbo-YQfwXM1Yi-rGTttOuNJUpUE Private Key: DGtRu68SmAxng-xx3BiTCa9NrztQbsSoECRuzry9VD4 ``` ### 2. Configure Backend Add to backend environment variables: ```env VAPID_SUBJECT=mailto:contact@placebo.mk VAPID_PUBLIC_KEY= VAPID_PRIVATE_KEY= ``` ### 3. Verify Setup 1. Deploy backend with VAPID keys 2. Visit: `https://api.placebo.mk/api/v1/push/vapid-public-key` 3. Should return JSON with your public key ### 4. Test in PWA 1. Open `https://app.placebo.mk` 2. Wait for notification banner 3. Click "Вклучи" (Enable) 4. Accept browser permission 5. Verify subscription in admin dashboard --- ## Post-Deployment Checklist ### Backend - [ ] Health check passing: `https://api.placebo.mk/health` - [ ] API docs accessible: `https://api.placebo.mk/api/v1` - [ ] Database connected (check logs) - [ ] JWT authentication working - [ ] CORS accepting frontend origins - [ ] Push notification endpoint working ### Frontend - [ ] Site loads: `https://placebo.mk` - [ ] No console errors - [ ] API calls successful (check Network tab) - [ ] Images and assets loading - [ ] Navigation working correctly - [ ] Admin dashboard accessible ### PWA - [ ] Site loads: `https://app.placebo.mk` - [ ] Service worker registered (DevTools → Application) - [ ] Install prompt appears - [ ] Notification banner shows - [ ] Push subscription successful - [ ] Works offline (cached resources) ### CMS - [ ] Admin panel: `https://cms.placebo.mk/admin` - [ ] First admin user created - [ ] API token generated for backend - [ ] Media uploads working - [ ] Content types created --- ## Troubleshooting ### Backend Won't Start **Symptoms:** Container keeps restarting **Solutions:** 1. Check logs: Coolify → Service → Logs 2. Verify database connection: - `DATABASE_HOST` should be service name (e.g., `placebo-db`) - Check password matches database password 3. Verify all required env vars are set 4. Check JWT_SECRET is at least 32 characters ### Frontend Shows Blank Page **Symptoms:** Page loads but nothing displays **Solutions:** 1. Check browser console for errors 2. Verify build args were set: - `VITE_API_URL` must be set at build time 3. Check Network tab for failed API calls 4. Rebuild with correct environment variables ### CORS Errors **Symptoms:** Browser blocks API requests **Solutions:** 1. Add all frontend domains to `CORS_ORIGIN`: ```env CORS_ORIGIN=https://placebo.mk,https://www.placebo.mk,https://app.placebo.mk ``` 2. Restart backend after changing 3. Clear browser cache ### Push Notifications Not Working **Symptoms:** Subscription fails or notifications don't arrive **Solutions:** 1. Verify HTTPS is enabled (required for push) 2. Check VAPID keys are set in backend 3. Test endpoint: `/api/v1/push/vapid-public-key` 4. Check service worker is registered 5. Verify browser notification permission is granted ### Database Connection Issues **Symptoms:** Backend can't connect to database **Solutions:** 1. Verify database service is running 2. Check hostname matches database service name 3. Verify credentials are correct 4. Ensure both services are on the same network 5. Check database logs for errors ### CMS Admin Not Accessible **Symptoms:** 404 or redirect loop on `/admin` **Solutions:** 1. Verify all Strapi secrets are set 2. Check `HOST=0.0.0.0` is set 3. Clear browser cache and cookies 4. Check CMS logs for startup errors --- ## Maintenance ### Updating Deployments 1. Push changes to GitHub main branch 2. Coolify → Service → **Redeploy** 3. Or enable automatic deployments on push ### Database Backups 1. Coolify → Database → **Backups** 2. Enable scheduled backups 3. Recommended retention: 7-30 days 4. Store backups off-server periodically ### Log Management - **View logs:** Coolify → Service → Logs - **Download logs:** Available in service settings - **Log rotation:** Configured automatically ### Monitoring 1. Set up Coolify notifications for: - Service down - High resource usage - Deployment failures 2. Monitor disk usage (uploads, logs) 3. Check SSL certificate expiration --- ## Security Checklist - [ ] All secrets are 32+ characters - [ ] HTTPS enabled on all services - [ ] Database not exposed to internet - [ ] CORS restricted to your domains only - [ ] CMS admin protected (consider IP whitelist) - [ ] Regular security updates applied - [ ] Backups configured and tested - [ ] Monitoring and alerts enabled --- ## Support For issues specific to: - **Coolify**: [Coolify Documentation](https://coolify.io/docs) - **NestJS**: [NestJS Documentation](https://docs.nestjs.com) - **Strapi**: [Strapi Documentation](https://docs.strapi.io) - **Vite/PWA**: [Vite PWA Plugin](https://vite-pwa-org.netlify.app)