648 lines
16 KiB
Markdown
648 lines
16 KiB
Markdown
# 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 | `<generate-secure-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: <your-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=<your-db-password>
|
|
DATABASE_NAME=placebo_db
|
|
DATABASE_SYNCHRONIZE=false
|
|
DATABASE_LOGGING=false
|
|
|
|
# JWT Authentication
|
|
JWT_SECRET=<generate-64-char-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=<your-strapi-api-token>
|
|
|
|
# Push Notifications
|
|
VAPID_SUBJECT=mailto:contact@placebo.mk
|
|
VAPID_PUBLIC_KEY=<generated-public-key>
|
|
VAPID_PRIVATE_KEY=<generated-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=<your-db-password>
|
|
DATABASE_SSL=false
|
|
|
|
# Security Keys (generate unique values for each)
|
|
APP_KEYS=<generate-4-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=<your-public-key>
|
|
VAPID_PRIVATE_KEY=<your-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)
|