Skip to content

Commit 0252567

Browse files
committed
feat: migrate to traefik and redis for scaling and update docs
1 parent d2c8fdb commit 0252567

8 files changed

Lines changed: 174 additions & 138 deletions

File tree

DEPLOYMENT.md

Lines changed: 21 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -20,46 +20,38 @@ Access:
2020
JWT_SECRET=your-secret-here docker compose -f docker-compose.prod.yml up -d --build
2121
```
2222

23-
Access everything through the client port:
23+
Access everything through the entry point port:
2424
- **App**: http://localhost:3080
2525
- **API**: http://localhost:3080/api
2626
- **Swagger**: http://localhost:3080/api-docs
2727
- **Mongo Express**: http://localhost:8081
2828

29-
The client's nginx proxies API/Socket requests internally.
29+
Traefik handles routing and sticky sessions for Socket.io.
3030

3131
### Environment Variables
3232

3333
| Variable | Required | Default | Description |
3434
|----------|----------|---------|-------------|
3535
| `JWT_SECRET` | **Yes** | - | Secret for JWT tokens |
3636
| `CLIENT_ORIGIN` | No | `*` | CORS origins (comma-separated for multiple) |
37-
| `CLIENT_PORT` | No | `3080` | Host port for frontend |
37+
| `CLIENT_PORT` | No | `3080` | Host port for frontend/entry point |
38+
| `REDIS_URL` | No | `redis://redis:6379` | Internal Redis URL |
39+
| `CANVAS_WIDTH` | No | `64` | Default canvas width |
40+
| `CANVAS_HEIGHT` | No | `64` | Default canvas height |
3841
| `ME_PORT` | No | `8081` | Host port for Mongo Express |
3942
| `ME_USERNAME` | No | `admin` | Mongo Express username |
4043
| `ME_PASSWORD` | No | `changeme` | Mongo Express password |
41-
| `VITE_API_URL` | No | `` | API URL (only if exposing API on separate domain) |
44+
| `VITE_API_URL` | No | `` | API URL (used at build time for client) |
4245

43-
### Portainer + Nginx Proxy Manager
46+
### Portainer + Reverse Proxy
4447

45-
The client's nginx proxies API/Socket internally, so you only need to expose the app itself.
48+
Traefik handles internal routing, so you only need to expose the Traefik entry point.
4649

4750
**Minimal setup (recommended):**
4851

4952
| Domain | Forward To | Port | WebSocket |
5053
|--------|------------|------|-----------|
51-
| `app.example.com` | `pixie-client` | 3080 | **Yes** |
52-
53-
That's it. API, Swagger, and Socket.io are all proxied through the client.
54-
55-
**Optional: Expose additional services**
56-
57-
If you want direct access to other services (NPM must be on the same Docker network, or add port mappings to compose):
58-
59-
| Domain | Forward To | Port | WebSocket |
60-
|--------|------------|------|-----------|
61-
| `api.example.com` | `pixie-server` | 3000 | **Yes** |
62-
| `mongo.example.com` | `pixie-mongo-express` | 8081 | No |
54+
| `app.example.com` | `pixie-traefik` | 80 | **Yes** |
6355

6456
**Steps:**
6557

@@ -71,41 +63,21 @@ If you want direct access to other services (NPM must be on the same Docker netw
7163
CLIENT_ORIGIN=https://app.example.com
7264
ME_PASSWORD=secure-password
7365
```
74-
- If exposing API separately, add it to CORS: `CLIENT_ORIGIN=https://app.example.com,https://api.example.com`
75-
76-
2. **Create NPM Proxy Host**
77-
- Domain: `app.example.com`
78-
- Forward: `pixie-client` port `3080`
79-
- Enable **Websockets Support**
80-
- Add SSL via Let's Encrypt
8166
82-
3. **SSL**: Use NPM's built-in Let's Encrypt for HTTPS.
83-
84-
### CORS Configuration
85-
86-
`CLIENT_ORIGIN` supports:
87-
- Single origin: `https://app.example.com`
88-
- Multiple origins: `https://app.example.com,https://api.example.com`
89-
- Allow all: `*` (not recommended for production)
90-
91-
Include both your app domain and API domain if Swagger needs to make requests.
67+
2. **Reverse Proxy (Nginx Proxy Manager / Caddy)**
68+
- Point your domain to the Docker host.
69+
- Forward traffic to the Docker host on port `3080` (or your configured `CLIENT_PORT`), or directly to the `pixie-traefik` container on port `80` if the proxy is on the same Docker network.
70+
- Enable **Websockets Support**.
9271
9372
### Architecture
9473
95-
**Standalone** (single entry point):
96-
```
97-
localhost:3080 ──► pixie-client (nginx)
98-
├── / → static files
99-
├── /api/ → proxy to pixie-server:3000
100-
├── /api-docs → proxy to pixie-server:3000
101-
└── /socket.io/ → proxy to pixie-server:3000 (WebSocket)
102-
```
103-
104-
**With NPM** (separate domains):
74+
**Standalone** (Traefik as entry point):
10575
```
106-
app.example.com ──► NPM ──► pixie-client:3080
107-
api.example.com ──► NPM ──► pixie-server:3000 (enable WebSocket)
108-
mongo.example.com ──► NPM ──► pixie-mongo-express:8081
76+
localhost:3080 ──► pixie-traefik
77+
├── / → pixie-client:80
78+
├── /api/ → pixie-server:3000
79+
├── /api-docs → pixie-server:3000
80+
└── /socket.io/ → pixie-server:3000 (WebSocket + Sticky Sessions)
10981
```
11082
111-
MongoDB is **not exposed** externally—only accessible within the Docker network.
83+
MongoDB and Redis are **not exposed** externally—only accessible within the Docker network.

README.md

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,21 +33,31 @@ docker compose up -d --scale server=3
3333

3434
| Service | URL |
3535
|---------|-----|
36-
| App | http://localhost:5173 |
37-
| API Docs | http://localhost:3000/api-docs |
36+
| App | http://localhost:3080 |
37+
| API Docs | http://localhost:3080/api-docs |
3838

39-
> 💡 **Dynamic Scaling:** You can add more server instances at any time using `--scale server=N`. Nginx is configured with `least_conn` to balance active connections across all instances.
39+
> 💡 **Dynamic Scaling:** You can add more server instances at any time using `--scale server=N`. Traefik is configured with `least_conn` and sticky sessions to balance active connections across all instances.
4040
4141
### Manual
4242

4343
```bash
44+
# Prerequisites: MongoDB and Redis running locally
45+
4446
# Server
4547
cd server && npm install && npm run dev
4648

4749
# Client (separate terminal)
4850
cd client && npm install && npm run dev
4951
```
5052

53+
### Testing
54+
55+
Run the full test suite (requires Docker):
56+
```bash
57+
npm test
58+
```
59+
This script handles temporary infrastructure (Redis) automatically.
60+
5161
### Production
5262

5363
```bash
@@ -64,14 +74,15 @@ JWT_SECRET=your-secret docker compose -f docker-compose.prod.yml up -d --build
6474
|----------|---------|-------------|
6575
| `JWT_SECRET` | `dev-secret-key` | **Required in production** |
6676
| `MONGO_URI` | `mongodb://localhost:27017/pixie` | Database connection |
77+
| `REDIS_URL` | `redis://localhost:6379` | Redis connection for scaling |
6778
| `PORT` | `3000` | Server port |
6879

6980
---
7081

7182
## 🛠️ Built With
7283

7384
**Frontend:** Vue.js 3 • Pinia • Vite • Socket.IO
74-
**Backend:** Node.js • Express • MongoDB • Socket.IO
85+
**Backend:** Node.js • Express • MongoDB • Redis • Socket.IO
7586

7687
---
7788

client/nginx.conf

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -30,32 +30,5 @@ http {
3030
index index.html index.htm;
3131
try_files $uri $uri/ /index.html;
3232
}
33-
34-
# Proxy API requests to the backend
35-
location /api/ {
36-
proxy_pass http://pixie-server:3000;
37-
proxy_set_header Host $host;
38-
proxy_set_header X-Real-IP $remote_addr;
39-
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
40-
proxy_set_header X-Forwarded-Proto $scheme;
41-
}
42-
43-
# Proxy Swagger UI
44-
location /api-docs {
45-
proxy_pass http://pixie-server:3000/api-docs;
46-
proxy_set_header Host $host;
47-
proxy_set_header X-Real-IP $remote_addr;
48-
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
49-
proxy_set_header X-Forwarded-Proto $scheme;
50-
}
51-
52-
# Proxy Socket.io (WebSocket)
53-
location /socket.io/ {
54-
proxy_pass http://pixie-server:3000;
55-
proxy_http_version 1.1;
56-
proxy_set_header Upgrade $http_upgrade;
57-
proxy_set_header Connection "upgrade";
58-
proxy_set_header Host $host;
59-
}
6033
}
6134
}

docker-compose.prod.yml

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
#
33
# Usage: JWT_SECRET=your-secret docker compose -f docker-compose.prod.yml up -d --build
44
#
5-
# Access: http://localhost:3080 (app + API + Swagger via nginx proxy)
5+
# Access: http://localhost:3080 (app + API + Swagger via Traefik proxy)
66
#
77
# For Portainer + NPM: proxy app.example.com -> pixie-client:3080 (enable WebSocket)
88
#
@@ -34,21 +34,36 @@ services:
3434
networks:
3535
- pixie-net
3636

37+
redis:
38+
image: redis:7-alpine
39+
container_name: pixie-redis
40+
restart: unless-stopped
41+
networks:
42+
- pixie-net
43+
3744
server:
3845
build:
3946
context: ./server
4047
target: production-stage
4148
image: pixie-server:latest
42-
container_name: pixie-server
4349
restart: unless-stopped
50+
labels:
51+
- "traefik.enable=true"
52+
- "traefik.http.routers.server.rule=PathPrefix(`/api`) || PathPrefix(`/socket.io`) || PathPrefix(`/api-docs`)"
53+
- "traefik.http.services.server.loadbalancer.server.port=3000"
54+
- "traefik.http.services.server.loadbalancer.strategy=p2c"
55+
- "traefik.http.services.server.loadbalancer.sticky.cookie=true"
56+
- "traefik.http.services.server.loadbalancer.sticky.cookie.name=PIXIE_SESSION"
4457
environment:
4558
PORT: 3000
4659
MONGO_URI: mongodb://mongo:27017/pixie
60+
REDIS_URL: redis://redis:6379
4761
CLIENT_ORIGIN: ${CLIENT_ORIGIN:-*}
4862
JWT_SECRET: ${JWT_SECRET:?JWT_SECRET is required}
4963
NODE_ENV: production
5064
depends_on:
5165
- mongo
66+
- redis
5267
networks:
5368
- pixie-net
5469

@@ -59,10 +74,27 @@ services:
5974
args:
6075
VITE_API_URL: ${VITE_API_URL:-}
6176
image: pixie-client:latest
62-
container_name: pixie-client
6377
restart: unless-stopped
78+
labels:
79+
- "traefik.enable=true"
80+
- "traefik.http.routers.client.rule=PathPrefix(`/`)"
81+
- "traefik.http.services.client.loadbalancer.server.port=80"
82+
networks:
83+
- pixie-net
84+
85+
traefik:
86+
image: traefik:latest
87+
container_name: pixie-traefik
88+
restart: unless-stopped
89+
command:
90+
- "--providers.docker=true"
91+
- "--providers.docker.endpoint=unix:///var/run/docker.sock"
92+
- "--providers.docker.exposedbydefault=false"
93+
- "--entrypoints.web.address=:80"
6494
ports:
6595
- "${CLIENT_PORT:-3080}:80"
96+
volumes:
97+
- /var/run/docker.sock:/var/run/docker.sock:ro
6698
networks:
6799
- pixie-net
68100

docker-compose.yml

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,13 @@ services:
3535
context: ./server
3636
target: dev-stage
3737
image: pixie-server:dev
38-
deploy:
39-
endpoint_mode: dnsrr
38+
labels:
39+
- "traefik.enable=true"
40+
- "traefik.http.routers.server.rule=PathPrefix(`/api`) || PathPrefix(`/socket.io`) || PathPrefix(`/api-docs`)"
41+
- "traefik.http.services.server.loadbalancer.server.port=3000"
42+
- "traefik.http.services.server.loadbalancer.strategy=p2c"
43+
- "traefik.http.services.server.loadbalancer.sticky.cookie=true"
44+
- "traefik.http.services.server.loadbalancer.sticky.cookie.name=PIXIE_SESSION"
4045
environment:
4146
PORT: 3000
4247
MONGO_URI: mongodb://mongo:27017/pixie
@@ -53,15 +58,20 @@ services:
5358
- pixie-net
5459
command: npm run dev
5560

56-
nginx:
57-
image: nginx:alpine
58-
container_name: pixie-nginx
61+
traefik:
62+
image: traefik:latest
63+
container_name: pixie-traefik
64+
command:
65+
- "--api.insecure=true"
66+
- "--providers.docker=true"
67+
- "--providers.docker.endpoint=unix:///var/run/docker.sock"
68+
- "--providers.docker.exposedbydefault=false"
69+
- "--entrypoints.web.address=:3000"
5970
ports:
60-
- "3000:80"
71+
- "3000:3000"
72+
- "8080:8080"
6173
volumes:
62-
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
63-
depends_on:
64-
- server
74+
- /var/run/docker.sock:/var/run/docker.sock:ro
6575
networks:
6676
- pixie-net
6777

@@ -78,7 +88,7 @@ services:
7888
- ./client/src:/app/src
7989
- ./client/index.html:/app/index.html
8090
depends_on:
81-
- nginx
91+
- traefik
8292
networks:
8393
- pixie-net
8494
command: npm run dev -- --host

nginx/nginx.conf

Lines changed: 0 additions & 45 deletions
This file was deleted.

0 commit comments

Comments
 (0)