added docker support
This commit is contained in:
46
DOKPLOY.md
Normal file
46
DOKPLOY.md
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
# Dokploy Deployment
|
||||||
|
|
||||||
|
## Assumptions
|
||||||
|
|
||||||
|
- **Frontend**: React Vite SPA built with Bun, served by nginx. Single public entry point.
|
||||||
|
- **Backend**: FastAPI, internal only. No Traefik labels, no public ports.
|
||||||
|
- **Routing**: nginx proxies `/api` to backend. Traefik routes all traffic to frontend (port 80).
|
||||||
|
- **Domain**: Replace `your-domain.com` in Traefik labels with your actual domain.
|
||||||
|
|
||||||
|
## Build & Run Locally
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Create network (Dokploy provides this)
|
||||||
|
docker network create dokploy-network 2>/dev/null || true
|
||||||
|
|
||||||
|
# Build and run
|
||||||
|
docker compose up --build
|
||||||
|
```
|
||||||
|
|
||||||
|
Frontend will be on port 80 (or the mapped port). Backend is internal.
|
||||||
|
|
||||||
|
## Dokploy Setup
|
||||||
|
|
||||||
|
1. Create `dokploy-network` if not exists:
|
||||||
|
```bash
|
||||||
|
docker network create dokploy-network
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Deploy via Dokploy UI: add project, use this `docker-compose.yml`.
|
||||||
|
|
||||||
|
3. Set environment variables in Dokploy:
|
||||||
|
- `CORS_ORIGINS`: Comma-separated allowed origins (e.g. `https://your-domain.com`)
|
||||||
|
|
||||||
|
4. Update Traefik labels in `docker-compose.yml`:
|
||||||
|
- Replace `your-domain.com` with your domain
|
||||||
|
- Ensure `certResolver=letsencrypt` matches your Traefik config
|
||||||
|
|
||||||
|
## Port Rules
|
||||||
|
|
||||||
|
- **Frontend**: Exposes port 80 internally. Traefik routes to it.
|
||||||
|
- **Backend**: No ports exposed. Communicates via `dokploy-network`.
|
||||||
|
|
||||||
|
## Service Communication
|
||||||
|
|
||||||
|
- Frontend (nginx) proxies `/api` to `http://backend:8000`
|
||||||
|
- Backend is reachable only from within the Docker network
|
||||||
10
backend/.dockerignore
Normal file
10
backend/.dockerignore
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
.venv
|
||||||
|
__pycache__
|
||||||
|
*.pyc
|
||||||
|
*.pyo
|
||||||
|
.pytest_cache
|
||||||
|
.coverage
|
||||||
|
htmlcov
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
*.md
|
||||||
27
backend/Dockerfile
Normal file
27
backend/Dockerfile
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# syntax=docker/dockerfile:1
|
||||||
|
FROM python:3.12-alpine AS builder
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
RUN pip install --no-cache-dir --upgrade pip
|
||||||
|
|
||||||
|
COPY requirements.txt .
|
||||||
|
RUN pip wheel --no-cache-dir --wheel-dir /wheels -r requirements.txt
|
||||||
|
|
||||||
|
# ---
|
||||||
|
FROM python:3.12-alpine AS runtime
|
||||||
|
|
||||||
|
RUN addgroup -g 1001 -S appgroup && adduser -S appuser -u 1001 -G appgroup
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY --from=builder /wheels /wheels
|
||||||
|
RUN pip install --no-cache-dir /wheels/* && rm -rf /wheels
|
||||||
|
|
||||||
|
COPY --chown=appuser:appgroup main.py .
|
||||||
|
|
||||||
|
USER appuser
|
||||||
|
|
||||||
|
EXPOSE 8000
|
||||||
|
|
||||||
|
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import os
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
|
||||||
@@ -7,9 +8,10 @@ app = FastAPI(
|
|||||||
version="0.1.0",
|
version="0.1.0",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
_cors_origins = os.getenv("CORS_ORIGINS", "http://localhost:5173").split(",")
|
||||||
app.add_middleware(
|
app.add_middleware(
|
||||||
CORSMiddleware,
|
CORSMiddleware,
|
||||||
allow_origins=["http://localhost:5173"],
|
allow_origins=[o.strip() for o in _cors_origins if o.strip()],
|
||||||
allow_credentials=True,
|
allow_credentials=True,
|
||||||
allow_methods=["*"],
|
allow_methods=["*"],
|
||||||
allow_headers=["*"],
|
allow_headers=["*"],
|
||||||
|
|||||||
30
docker-compose.yml
Normal file
30
docker-compose.yml
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
services:
|
||||||
|
backend:
|
||||||
|
build:
|
||||||
|
context: ./backend
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
restart: always
|
||||||
|
networks:
|
||||||
|
- dokploy-network
|
||||||
|
|
||||||
|
frontend:
|
||||||
|
build:
|
||||||
|
context: ./frontend
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- 80
|
||||||
|
networks:
|
||||||
|
- dokploy-network
|
||||||
|
depends_on:
|
||||||
|
- backend
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.frontend.rule=Host(`your-domain.com`)"
|
||||||
|
- "traefik.http.routers.frontend.entrypoints=websecure"
|
||||||
|
- "traefik.http.routers.frontend.tls.certResolver=letsencrypt"
|
||||||
|
- "traefik.http.services.frontend.loadbalancer.server.port=80"
|
||||||
|
|
||||||
|
networks:
|
||||||
|
dokploy-network:
|
||||||
|
external: true
|
||||||
7
frontend/.dockerignore
Normal file
7
frontend/.dockerignore
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
.git
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
*.md
|
||||||
|
.DS_Store
|
||||||
20
frontend/Dockerfile
Normal file
20
frontend/Dockerfile
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# syntax=docker/dockerfile:1
|
||||||
|
FROM oven/bun:1-alpine AS builder
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY package.json bun.lock* package-lock.json* pnpm-lock.yaml* yarn.lock* ./
|
||||||
|
RUN bun install --frozen-lockfile || bun install
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
RUN bun run build
|
||||||
|
|
||||||
|
# ---
|
||||||
|
FROM nginx:alpine AS runtime
|
||||||
|
|
||||||
|
COPY --from=builder /app/dist /usr/share/nginx/html
|
||||||
|
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
|
|
||||||
|
EXPOSE 80
|
||||||
|
|
||||||
|
CMD ["nginx", "-g", "daemon off;"]
|
||||||
23
frontend/nginx.conf
Normal file
23
frontend/nginx.conf
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name _;
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /api {
|
||||||
|
rewrite ^/api/?(.*) /$1 break;
|
||||||
|
proxy_pass http://backend: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;
|
||||||
|
}
|
||||||
|
|
||||||
|
gzip on;
|
||||||
|
gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user