Create DEPLOYMENT.md
This commit is contained in:
219
docs/DEPLOYMENT.md
Normal file
219
docs/DEPLOYMENT.md
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
# Deployment Guide for Grateful Journal
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This guide covers deploying the Grateful Journal Docker stack to a production server. The app requires HTTPS — the Web Crypto API used for end-to-end encryption is blocked by browsers on plain HTTP.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Deployment Options
|
||||||
|
|
||||||
|
### Option 1: VPS (Recommended) — DigitalOcean, Hetzner, Linode, Vultr
|
||||||
|
|
||||||
|
Full control. Run Docker Compose directly on the server behind a reverse proxy.
|
||||||
|
|
||||||
|
**Minimum specs:** 1 vCPU, 1 GB RAM, 20 GB disk
|
||||||
|
|
||||||
|
**Steps:**
|
||||||
|
1. Provision a server running Ubuntu 22.04+
|
||||||
|
2. Install Docker and Docker Compose
|
||||||
|
3. Point your domain DNS A record to the server IP
|
||||||
|
4. Set up a reverse proxy with SSL (see Reverse Proxy section below)
|
||||||
|
5. Clone the repo and configure environment files
|
||||||
|
6. Run `docker compose up --build -d`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Option 2: Railway / Render / Fly.io
|
||||||
|
|
||||||
|
Platform-as-a-service. Easier setup but less control. These platforms handle SSL automatically.
|
||||||
|
|
||||||
|
- **Railway** — supports Docker Compose directly, good free tier
|
||||||
|
- **Render** — supports Docker, free tier available but spins down on inactivity
|
||||||
|
- **Fly.io** — supports Docker, generous free tier, good global distribution
|
||||||
|
|
||||||
|
Note: MongoDB on these platforms should be replaced with MongoDB Atlas (managed) since persistent volumes can be unreliable on free tiers.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Option 3: Cloud VM (AWS EC2, GCP Compute, Azure VM)
|
||||||
|
|
||||||
|
Same as VPS but on a major cloud provider. More expensive for small apps but useful if you're already in that ecosystem.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Reverse Proxy Setup (Required for HTTPS)
|
||||||
|
|
||||||
|
The frontend container must not be exposed directly. A reverse proxy handles SSL termination and forwards traffic to the frontend container.
|
||||||
|
|
||||||
|
### Using Nginx + Certbot (Let's Encrypt)
|
||||||
|
|
||||||
|
Install on the host (not inside Docker):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt install nginx certbot python3-certbot-nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
Change `docker-compose.yml` to bind frontend to localhost only:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:8000:80"
|
||||||
|
```
|
||||||
|
|
||||||
|
Create `/etc/nginx/sites-available/grateful-journal`:
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name yourdomain.com;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://127.0.0.1:8000;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Enable and get SSL certificate:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo ln -s /etc/nginx/sites-available/grateful-journal /etc/nginx/sites-enabled/
|
||||||
|
sudo certbot --nginx -d yourdomain.com
|
||||||
|
sudo systemctl reload nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
Certbot auto-renews the certificate. Done — the app is now on HTTPS.
|
||||||
|
|
||||||
|
### Using Traefik (Docker-native alternative)
|
||||||
|
|
||||||
|
Traefik runs as a Docker container and handles SSL automatically via Let's Encrypt. Better if you want everything inside Docker. Requires adding a `traefik` service to `docker-compose.yml` with labels on the frontend service.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Environment Changes for Production
|
||||||
|
|
||||||
|
### `backend/.env`
|
||||||
|
|
||||||
|
```env
|
||||||
|
MONGODB_URI=mongodb://mongo:27017
|
||||||
|
MONGODB_DB_NAME=grateful_journal
|
||||||
|
API_PORT=8001
|
||||||
|
ENVIRONMENT=production
|
||||||
|
FRONTEND_URL=https://yourdomain.com
|
||||||
|
```
|
||||||
|
|
||||||
|
- Change `FRONTEND_URL` to your actual domain with `https://`
|
||||||
|
- This is used for CORS — must match exactly what the browser sends as the Origin header
|
||||||
|
|
||||||
|
### Root `.env` (frontend build args)
|
||||||
|
|
||||||
|
```env
|
||||||
|
VITE_FIREBASE_API_KEY=...
|
||||||
|
VITE_FIREBASE_AUTH_DOMAIN=...
|
||||||
|
VITE_FIREBASE_PROJECT_ID=...
|
||||||
|
VITE_FIREBASE_STORAGE_BUCKET=...
|
||||||
|
VITE_FIREBASE_MESSAGING_SENDER_ID=...
|
||||||
|
VITE_FIREBASE_APP_ID=...
|
||||||
|
VITE_API_URL=/api
|
||||||
|
```
|
||||||
|
|
||||||
|
- `VITE_API_URL=/api` stays as-is — nginx proxy handles routing
|
||||||
|
- Firebase keys stay the same unless you create a separate Firebase project for production
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Firebase Configuration
|
||||||
|
|
||||||
|
Firebase requires your production domain to be added as an **authorized domain** for Google Sign-In.
|
||||||
|
|
||||||
|
1. Go to [Firebase Console](https://console.firebase.google.com)
|
||||||
|
2. Select your project → Authentication → Settings → Authorized domains
|
||||||
|
3. Add `yourdomain.com`
|
||||||
|
|
||||||
|
Without this, Google sign-in will fail on the production domain.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## MongoDB Security
|
||||||
|
|
||||||
|
The current setup has no MongoDB authentication — fine for local dev, not for production.
|
||||||
|
|
||||||
|
Add a MongoDB username and password:
|
||||||
|
|
||||||
|
### `docker-compose.yml` — add environment to mongo service:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
mongo:
|
||||||
|
image: mongo:6
|
||||||
|
environment:
|
||||||
|
MONGO_INITDB_ROOT_USERNAME: admin
|
||||||
|
MONGO_INITDB_ROOT_PASSWORD: your_strong_password
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
### `backend/.env` — update the connection string:
|
||||||
|
|
||||||
|
```env
|
||||||
|
MONGODB_URI=mongodb://admin:your_strong_password@mongo:27017
|
||||||
|
```
|
||||||
|
|
||||||
|
Use a strong random password. Store it securely (not in git).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Keeping Secrets Out of Git
|
||||||
|
|
||||||
|
Never commit `.env` files with real credentials. Before deploying:
|
||||||
|
|
||||||
|
- Add `.env` and `backend/.env` to `.gitignore` (already done)
|
||||||
|
- On the server, create the `.env` files manually or via a secrets manager
|
||||||
|
- Use environment variables injected by the platform if using Railway/Render/Fly.io
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Data Backups
|
||||||
|
|
||||||
|
MongoDB data lives in the `mongo_data` Docker volume. Back it up regularly:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Dump
|
||||||
|
docker exec grateful-journal-mongo-1 mongodump --out /data/backup
|
||||||
|
docker cp grateful-journal-mongo-1:/data/backup ./mongo-backup
|
||||||
|
|
||||||
|
# Restore
|
||||||
|
docker cp ./mongo-backup grateful-journal-mongo-1:/data/backup
|
||||||
|
docker exec grateful-journal-mongo-1 mongorestore /data/backup
|
||||||
|
```
|
||||||
|
|
||||||
|
For automated backups, set up a cron job or use MongoDB Atlas which has built-in backups.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Deploying Updates
|
||||||
|
|
||||||
|
After pushing code changes to the server:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git pull
|
||||||
|
docker compose up --build -d
|
||||||
|
```
|
||||||
|
|
||||||
|
This rebuilds only changed images and replaces containers with zero manual steps.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Pre-Deployment Checklist
|
||||||
|
|
||||||
|
- [ ] Domain DNS pointing to server IP
|
||||||
|
- [ ] HTTPS set up via reverse proxy
|
||||||
|
- [ ] `FRONTEND_URL` updated to production domain in `backend/.env`
|
||||||
|
- [ ] Production domain added to Firebase authorized domains
|
||||||
|
- [ ] MongoDB authentication enabled
|
||||||
|
- [ ] `.env` files not committed to git
|
||||||
|
- [ ] `docker-compose.yml` frontend port bound to `127.0.0.1:8000:80`
|
||||||
|
- [ ] MongoDB backup strategy in place
|
||||||
Reference in New Issue
Block a user