Skip to content

Commit 2b8bbf4

Browse files
authored
Merge pull request #50 from ArchitectVS7/codex/assess-deployment-feasibility-to-digitalocean
Codex-generated pull request
2 parents 76eb71a + 971a617 commit 2b8bbf4

1 file changed

Lines changed: 242 additions & 0 deletions

File tree

DEPLOYMENT_DIGITALOCEAN.md

Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
# DigitalOcean Deployment Assessment and Guide
2+
3+
## Feasibility Assessment (No Code Changes)
4+
5+
**Short answer: Yes, deployment is feasible without additional code changes.**
6+
7+
This repository already includes:
8+
9+
- A multi-service Docker Compose setup (`postgres`, `backend`, `frontend`).
10+
- Production-capable Dockerfiles for both backend and frontend.
11+
- Backend migration-on-start behavior.
12+
- Runtime configuration via environment variables.
13+
14+
### Important caveats to handle at deployment time
15+
16+
1. **Set production environment variables explicitly.**
17+
- You must override defaults for `JWT_SECRET`, `CORS_ORIGIN`, and API URLs.
18+
2. **Frontend API URL is build-time.**
19+
- `VITE_API_URL` is compiled into frontend assets during image build.
20+
3. **Backend CORS must include your public frontend origin.**
21+
- Set `CORS_ORIGIN=https://your-domain.com` (or comma-separated list).
22+
4. **Migration startup behavior exists.**
23+
- Backend runs `prisma migrate deploy` at startup; ensure DB credentials and migration history are correct before first production run.
24+
5. **Use a reverse proxy + TLS in front of services.**
25+
- Expose only 80/443 publicly and proxy to frontend/backend containers.
26+
27+
---
28+
29+
## Architecture Recommendation for a Droplet
30+
31+
- **Droplet**: Ubuntu 22.04+.
32+
- **Services**: Docker Compose stack from repo.
33+
- **Reverse proxy**: Nginx on host (or Caddy) for TLS termination.
34+
- **Domain**: `app.example.com` for frontend and either:
35+
- path-based API (`https://app.example.com/api`), or
36+
- subdomain API (`https://api.example.com`).
37+
- **Database**:
38+
- Option A: Compose PostgreSQL container (simple/single-host).
39+
- Option B: Managed PostgreSQL (more resilient, recommended for production).
40+
41+
---
42+
43+
## Step-by-Step Deployment (Docker Compose on Droplet)
44+
45+
## 1) Prepare DigitalOcean
46+
47+
1. Create a droplet (Ubuntu 22.04+), assign static IP.
48+
2. Add DNS records:
49+
- `A app.example.com -> <droplet-ip>`
50+
- Optional `A api.example.com -> <droplet-ip>`
51+
3. SSH into droplet as sudo user.
52+
53+
## 2) Install Docker Engine + Compose plugin
54+
55+
```bash
56+
sudo apt update
57+
sudo apt install -y ca-certificates curl gnupg
58+
sudo install -m 0755 -d /etc/apt/keyrings
59+
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
60+
sudo chmod a+r /etc/apt/keyrings/docker.gpg
61+
echo \
62+
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
63+
$(. /etc/os-release && echo $VERSION_CODENAME) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
64+
sudo apt update
65+
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
66+
sudo usermod -aG docker $USER
67+
# The following command applies the new group membership to the current shell session.
68+
# For the change to be permanent across all sessions, you need to log out and log back in.
69+
newgrp docker
70+
```
71+
72+
## 3) Clone repo and prepare deployment files
73+
74+
```bash
75+
git clone <your-repo-url> TaskMan
76+
cd TaskMan
77+
```
78+
79+
Create a production override file (do not edit base compose if you want to keep local defaults).
80+
81+
`docker-compose.prod.yml`:
82+
83+
```yaml
84+
services:
85+
backend:
86+
environment:
87+
NODE_ENV: production
88+
PORT: 4000
89+
DATABASE_URL: postgresql://taskapp:${POSTGRES_PASSWORD}@postgres:5432/taskapp?schema=public
90+
JWT_SECRET: ${JWT_SECRET}
91+
JWT_EXPIRES_IN: 7d
92+
CORS_ORIGIN: https://app.example.com
93+
restart: unless-stopped
94+
95+
frontend:
96+
build:
97+
context: ./frontend
98+
dockerfile: Dockerfile
99+
args:
100+
VITE_API_URL: https://app.example.com/api
101+
environment:
102+
PORT: 3000
103+
restart: unless-stopped
104+
105+
postgres:
106+
environment:
107+
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
108+
restart: unless-stopped
109+
```
110+
111+
Create `.env.prod`:
112+
113+
```dotenv
114+
POSTGRES_PASSWORD=<strong-random-password>
115+
JWT_SECRET=<long-random-secret>
116+
```
117+
118+
## 4) Start stack
119+
120+
```bash
121+
docker compose -f docker-compose.yml -f docker-compose.prod.yml --env-file .env.prod up -d --build
122+
```
123+
124+
Check health/logs:
125+
126+
```bash
127+
docker compose ps
128+
docker compose logs backend --tail=200
129+
docker compose logs frontend --tail=200
130+
```
131+
132+
Backend health endpoint:
133+
134+
```bash
135+
curl -f http://127.0.0.1:4000/health
136+
```
137+
138+
## 5) Install and configure Nginx with Let's Encrypt
139+
140+
Install:
141+
142+
```bash
143+
sudo apt install -y nginx certbot python3-certbot-nginx
144+
```
145+
146+
Example Nginx server block for `app.example.com` (`/etc/nginx/sites-available/taskman`):
147+
148+
```nginx
149+
server {
150+
listen 80;
151+
server_name app.example.com;
152+
153+
location / {
154+
proxy_pass http://127.0.0.1:3000;
155+
proxy_set_header Host $host;
156+
proxy_set_header X-Real-IP $remote_addr;
157+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
158+
proxy_set_header X-Forwarded-Proto $scheme;
159+
}
160+
161+
location /api/ {
162+
proxy_pass http://127.0.0.1:4000/;
163+
proxy_set_header Host $host;
164+
proxy_set_header X-Real-IP $remote_addr;
165+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
166+
proxy_set_header X-Forwarded-Proto $scheme;
167+
}
168+
169+
location /socket.io/ {
170+
proxy_pass http://127.0.0.1:4000/socket.io/;
171+
proxy_http_version 1.1;
172+
proxy_set_header Upgrade $http_upgrade;
173+
proxy_set_header Connection "upgrade";
174+
proxy_set_header Host $host;
175+
proxy_set_header X-Real-IP $remote_addr;
176+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
177+
proxy_set_header X-Forwarded-Proto $scheme;
178+
}
179+
}
180+
```
181+
182+
Enable and test:
183+
184+
```bash
185+
sudo ln -s /etc/nginx/sites-available/taskman /etc/nginx/sites-enabled/taskman
186+
sudo nginx -t
187+
sudo systemctl reload nginx
188+
```
189+
190+
Issue TLS cert:
191+
192+
```bash
193+
sudo certbot --nginx -d app.example.com
194+
```
195+
196+
## 6) Lock down public exposure
197+
198+
- Allow only `22`, `80`, `443` in cloud firewall / UFW.
199+
- Keep `3000`, `4000`, `5432` closed publicly.
200+
201+
Example with UFW:
202+
203+
```bash
204+
sudo ufw allow OpenSSH
205+
sudo ufw allow 80/tcp
206+
sudo ufw allow 443/tcp
207+
sudo ufw enable
208+
```
209+
210+
## 7) Validate production behavior
211+
212+
1. Open `https://app.example.com`.
213+
2. Register/login flow works.
214+
3. Task CRUD works.
215+
4. Realtime updates (socket) work.
216+
5. Backend docs/health reachable through proxy path if intended.
217+
218+
## 8) Updates / redeploy
219+
220+
```bash
221+
cd ~/TaskMan
222+
git pull
223+
docker compose -f docker-compose.yml -f docker-compose.prod.yml --env-file .env.prod up -d --build
224+
```
225+
226+
---
227+
228+
## Operational Checklist
229+
230+
- [ ] Strong secrets in `.env.prod`.
231+
- [ ] Correct frontend URL compiled (`VITE_API_URL`).
232+
- [ ] `CORS_ORIGIN` matches public frontend origin(s).
233+
- [ ] Backups configured (DB volume snapshots or managed DB backups).
234+
- [ ] HTTPS enabled and auto-renew working (`systemctl status certbot.timer`).
235+
- [ ] Container restart policy enabled.
236+
- [ ] Logs monitored (`docker compose logs -f`).
237+
238+
---
239+
240+
## Feasibility Conclusion
241+
242+
Deploying this codebase to a DigitalOcean droplet is **feasible today without code changes**, provided you supply production env values, run with Docker Compose overrides, and place Nginx + TLS in front of the containers.

0 commit comments

Comments
 (0)