mirror of
https://github.com/jhd3197/ServerKit.git
synced 2026-11-09 05:24:49 +09:00
Redesigned the README with improved layout, badges, architecture overview, and updated quick start instructions. Added a comprehensive docs/ARCHITECTURE.md file detailing system design, request flow, template system, port allocation, database linking, file paths, and troubleshooting.
561 lines
46 KiB
Markdown
561 lines
46 KiB
Markdown
# ServerKit Architecture
|
|
|
|
> Deep dive into how ServerKit connects domains, apps, containers, and databases.
|
|
|
|
---
|
|
|
|
## Table of Contents
|
|
|
|
- [System Overview](#system-overview)
|
|
- [Request Flow](#request-flow)
|
|
- [Template System](#template-system)
|
|
- [Port Allocation](#port-allocation)
|
|
- [Database Linking](#database-linking)
|
|
- [File Paths](#file-paths)
|
|
|
|
---
|
|
|
|
## System Overview
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ INTERNET │
|
|
└─────────────────────────────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
|
│ YOUR SERVER │
|
|
│ ┌───────────────────────────────────────────────────────────────────────────────────────────────┐ │
|
|
│ │ NGINX (Reverse Proxy) │ │
|
|
│ │ Port 80 / 443 │ │
|
|
│ │ │ │
|
|
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
|
|
│ │ │ app1.com │ │ app2.com │ │ api.app3.com │ │ Private URLs │ │ │
|
|
│ │ │ :443 │ │ :443 │ │ :443 │ │ /p/abc123 │ │ │
|
|
│ │ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │ │
|
|
│ └──────────┼───────────────────┼───────────────────┼───────────────────┼────────────────────────┘ │
|
|
│ │ │ │ │ │
|
|
│ │ proxy_pass │ proxy_pass │ proxy_pass │ proxy_pass │
|
|
│ ▼ ▼ ▼ ▼ │
|
|
│ ┌─────────────────────────────────────────────────────────────────────────────────────────────┐ │
|
|
│ │ DOCKER CONTAINERS │ │
|
|
│ │ │ │
|
|
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
|
|
│ │ │ WordPress │ │ Flask │ │ Node.js │ │ Custom │ │ │
|
|
│ │ │ Port 8001 │ │ Port 8002 │ │ Port 8003 │ │ Port 8004 │ │ │
|
|
│ │ │ │ │ │ │ │ │ │ │ │
|
|
│ │ │ ┌──────────┐ │ │ ┌──────────┐ │ │ ┌──────────┐ │ │ ┌──────────┐ │ │ │
|
|
│ │ │ │ Apache │ │ │ │ Gunicorn │ │ │ │ PM2 │ │ │ │ Your │ │ │ │
|
|
│ │ │ │ PHP-FPM │ │ │ │ Python │ │ │ │ Express │ │ │ │ App │ │ │ │
|
|
│ │ │ └──────────┘ │ │ └──────────┘ │ │ └──────────┘ │ │ └──────────┘ │ │ │
|
|
│ │ └──────┬───────┘ └──────────────┘ └──────────────┘ └──────────────┘ │ │
|
|
│ │ │ │ │
|
|
│ └──────────┼──────────────────────────────────────────────────────────────────────────────────┘ │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ ┌─────────────────────────────────────────────────────────────────────────────────────────────┐ │
|
|
│ │ DATABASES │ │
|
|
│ │ │ │
|
|
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
|
|
│ │ │ MySQL │ │ PostgreSQL │ │ Redis │ │ MongoDB │ │ │
|
|
│ │ │ Port 3306 │ │ Port 5432 │ │ Port 6379 │ │ Port 27017 │ │ │
|
|
│ │ └──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘ │ │
|
|
│ │ │ │
|
|
│ └──────────────────────────────────────────────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
└──────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## Request Flow
|
|
|
|
What happens when a user visits your app:
|
|
|
|
```
|
|
User Request What Happens
|
|
─────────────────────────────────────────────────────────────────────────────────
|
|
|
|
Browser 1. DNS resolves app1.com to your server IP
|
|
│
|
|
▼
|
|
┌─────────┐ 2. Request hits Nginx on port 80/443
|
|
│ Nginx │ Nginx checks server_name directives
|
|
│ :80/443 │ Matches "app1.com" → proxy_pass http://127.0.0.1:8001
|
|
└────┬────┘
|
|
│
|
|
▼
|
|
┌─────────┐ 3. Nginx forwards request to Docker container
|
|
│ Docker │ Container receives request on internal port
|
|
│ :8001 │ App processes and returns response
|
|
└────┬────┘
|
|
│
|
|
▼
|
|
┌─────────┐ 4. Response flows back through Nginx
|
|
│ Response│ SSL termination handled by Nginx
|
|
│ 200 OK │ User sees the page
|
|
└─────────┘
|
|
```
|
|
|
|
### Detailed Nginx → Container Flow
|
|
|
|
```
|
|
NGINX CONFIG DOCKER
|
|
/etc/nginx/sites-enabled/ CONTAINER
|
|
───────────────────────── ─────────────────
|
|
|
|
server {
|
|
listen 80;
|
|
server_name my-blog.com;
|
|
┌─────────────────┐
|
|
location / { │ WordPress │
|
|
proxy_pass ─────────────────────────► │ │
|
|
http://127.0.0.1:8001; │ 0.0.0.0:80 │
|
|
proxy_set_header Host $host; │ ▲ │
|
|
proxy_set_header X-Real-IP ...; │ │ │
|
|
} │ (mapped to │
|
|
} │ host:8001) │
|
|
└─────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## Template System
|
|
|
|
### Template → App → Domain Lifecycle
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────────────────────┐
|
|
│ TEMPLATE LIBRARY │
|
|
│ /etc/serverkit/templates/ │
|
|
│ │
|
|
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
|
|
│ │WordPress│ │ Flask │ │ Node.js │ │ Grafana │ │ n8n │ ... 60+ │
|
|
│ │ .yaml │ │ .yaml │ │ .yaml │ │ .yaml │ │ .yaml │ │
|
|
│ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ │
|
|
│ │ │ │ │ │ │
|
|
└────────┼────────────┼────────────┼────────────┼────────────┼───────────────────┘
|
|
│ │ │ │ │
|
|
│ User clicks "Deploy" in UI │
|
|
│ │ │
|
|
▼ ▼ ▼
|
|
┌─────────────────────────────────────────────────────────────────────────────────┐
|
|
│ SERVERKIT BACKEND │
|
|
│ │
|
|
│ TemplateService.install_template() │
|
|
│ ├── 1. Parse template YAML │
|
|
│ ├── 2. Generate unique port (8000-60000) │
|
|
│ ├── 3. Substitute variables: │
|
|
│ │ ${APP_NAME} → "my-blog" │
|
|
│ │ ${HTTP_PORT} → "8247" │
|
|
│ │ ${DB_PASSWORD} → "auto_generated" │
|
|
│ ├── 4. Create /var/serverkit/apps/my-blog/docker-compose.yml │
|
|
│ ├── 5. Run: docker compose up -d --build │
|
|
│ └── 6. Store app record in database │
|
|
│ │
|
|
└─────────────────────────────────────────────────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────────────────────────────────────────────────────────────────────────┐
|
|
│ APP CREATED │
|
|
│ │
|
|
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
|
|
│ │ App Name: my-blog │ │
|
|
│ │ Type: docker │ │
|
|
│ │ Port: 8247 (auto-assigned) │ │
|
|
│ │ Status: running │ │
|
|
│ │ Container: my-blog │ │
|
|
│ │ Path: /var/serverkit/apps/my-blog/ │ │
|
|
│ │ │ │
|
|
│ │ Private URL: http://server-ip:8247 ◄── Works immediately! │ │
|
|
│ └─────────────────────────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
└─────────────────────────────────────────────────────────────────────────────────┘
|
|
│
|
|
│ User clicks "Connect Domain"
|
|
▼
|
|
┌─────────────────────────────────────────────────────────────────────────────────┐
|
|
│ DOMAIN CONNECTED │
|
|
│ │
|
|
│ DomainService.create_domain() │
|
|
│ ├── 1. Validate domain DNS points to server │
|
|
│ ├── 2. Check container port is accessible │
|
|
│ ├── 3. Generate Nginx config: │
|
|
│ │ │
|
|
│ │ server { │
|
|
│ │ listen 80; │
|
|
│ │ server_name my-blog.com; │
|
|
│ │ │
|
|
│ │ location / { │
|
|
│ │ proxy_pass http://127.0.0.1:8247; ◄── Container port │
|
|
│ │ 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; │
|
|
│ │ } │
|
|
│ │ } │
|
|
│ │ │
|
|
│ ├── 4. Write to /etc/nginx/sites-available/my-blog │
|
|
│ ├── 5. Symlink to /etc/nginx/sites-enabled/my-blog │
|
|
│ ├── 6. Test config: nginx -t │
|
|
│ ├── 7. Reload: systemctl reload nginx │
|
|
│ └── 8. (Optional) Request SSL via Let's Encrypt │
|
|
│ │
|
|
│ Public URL: https://my-blog.com ◄── Now accessible worldwide! │
|
|
│ │
|
|
└─────────────────────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
### Template YAML Structure
|
|
|
|
```yaml
|
|
# Example: flask-hello-world.yaml
|
|
|
|
id: flask-hello-world
|
|
name: Flask - Hello World
|
|
version: "1.0"
|
|
description: Simple Flask debug app
|
|
categories:
|
|
- development
|
|
- api
|
|
|
|
variables:
|
|
- name: HTTP_PORT # Variable name
|
|
type: port # Auto-generates available port
|
|
default: "5000" # Starting port to search from
|
|
hidden: true # Don't show in UI
|
|
|
|
compose: # Docker Compose configuration
|
|
services:
|
|
app:
|
|
image: python:3.12-slim
|
|
container_name: ${APP_NAME}
|
|
ports:
|
|
- "${HTTP_PORT}:5000" # Host:Container port mapping
|
|
environment:
|
|
- APP_NAME=${APP_NAME}
|
|
- EXTERNAL_PORT=${HTTP_PORT}
|
|
```
|
|
|
|
---
|
|
|
|
## Port Allocation
|
|
|
|
### How ServerKit Finds Available Ports
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────────────────────┐
|
|
│ PORT ALLOCATION │
|
|
│ │
|
|
│ TemplateService._find_available_port(start=8000) │
|
|
│ │
|
|
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
|
|
│ │ Step 1: Check Database │ │
|
|
│ │ SELECT port FROM applications WHERE port IS NOT NULL │ │
|
|
│ │ Result: [8001, 8002, 8005, 8010] │ │
|
|
│ └─────────────────────────────────────────────────────────────────────────┘ │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
|
|
│ │ Step 2: Check Docker │ │
|
|
│ │ docker ps --format '{{.Ports}}' │ │
|
|
│ │ Parse: "0.0.0.0:8003->80/tcp" → 8003 │ │
|
|
│ │ Result: [8003, 8004] │ │
|
|
│ └─────────────────────────────────────────────────────────────────────────┘ │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
|
|
│ │ Step 3: Socket Bind Test │ │
|
|
│ │ Try: socket.bind(('127.0.0.1', port)) │ │
|
|
│ │ If fails → port in use by system │ │
|
|
│ └─────────────────────────────────────────────────────────────────────────┘ │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
|
|
│ │ Step 4: Return First Available │ │
|
|
│ │ │ │
|
|
│ │ Checking 8000... taken (DB) │ │
|
|
│ │ Checking 8001... taken (DB) │ │
|
|
│ │ Checking 8002... taken (DB) │ │
|
|
│ │ Checking 8003... taken (Docker) │ │
|
|
│ │ Checking 8004... taken (Docker) │ │
|
|
│ │ Checking 8005... taken (DB) │ │
|
|
│ │ Checking 8006... AVAILABLE ✓ │ │
|
|
│ │ │ │
|
|
│ │ Return: 8006 │ │
|
|
│ └─────────────────────────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
└─────────────────────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
### Port Map Example
|
|
|
|
```
|
|
┌──────────┬────────────────────────────────────────┐
|
|
│ Port │ Service │
|
|
├──────────┼────────────────────────────────────────┤
|
|
│ 22 │ SSH │
|
|
│ 80 │ Nginx (HTTP) │
|
|
│ 443 │ Nginx (HTTPS) │
|
|
│ 3306 │ MySQL │
|
|
│ 5432 │ PostgreSQL │
|
|
│ 5000 │ ServerKit Backend API │
|
|
│ 6379 │ Redis │
|
|
├──────────┼────────────────────────────────────────┤
|
|
│ 8001 │ App: wordpress-blog │
|
|
│ 8002 │ App: flask-api │
|
|
│ 8003 │ App: node-frontend │
|
|
│ 8004 │ App: grafana-monitoring │
|
|
│ 8005 │ App: n8n-automation │
|
|
│ 8006 │ App: (next available) │
|
|
│ ... │ ... │
|
|
│ 60000 │ (max port range) │
|
|
└──────────┴────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## Database Linking
|
|
|
|
### How Apps Connect to Databases
|
|
|
|
```
|
|
┌────────────────────────────────────────────────────────────────────────────────┐
|
|
│ │
|
|
│ ┌──────────────────┐ ┌──────────────────┐ │
|
|
│ │ APP (Flask) │ │ DATABASE (MySQL) │ │
|
|
│ │ │ │ │ │
|
|
│ │ Needs DB access │ │ db: my_app_db │ │
|
|
│ │ │ │ user: app_user │ │
|
|
│ └────────┬─────────┘ └────────┬─────────┘ │
|
|
│ │ │ │
|
|
│ │ User clicks "Link Database" │ │
|
|
│ │ │ │
|
|
│ └──────────────┬───────────────────┘ │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ ┌────────────────────────────────────────────────────────────────────────┐ │
|
|
│ │ SERVERKIT LINKS THEM │ │
|
|
│ │ │ │
|
|
│ │ 1. Creates database: my_app_db │ │
|
|
│ │ 2. Creates user with secure password │ │
|
|
│ │ 3. Grants permissions │ │
|
|
│ │ 4. Injects environment variables into app container: │ │
|
|
│ │ │ │
|
|
│ │ ┌─────────────────────────────────────────────────────────────┐ │ │
|
|
│ │ │ DB_HOST=localhost │ │ │
|
|
│ │ │ DB_PORT=3306 │ │ │
|
|
│ │ │ DB_NAME=my_app_db │ │ │
|
|
│ │ │ DB_USER=app_user │ │ │
|
|
│ │ │ DB_PASSWORD=xK9#mP2$vL7@nQ4 │ │ │
|
|
│ │ │ │ │ │
|
|
│ │ │ # Also provides connection URL format: │ │ │
|
|
│ │ │ DATABASE_URL=mysql://app_user:xK9#mP2$vL7@nQ4@localhost/db │ │ │
|
|
│ │ └─────────────────────────────────────────────────────────────┘ │ │
|
|
│ │ │ │
|
|
│ │ 5. Restarts app container to pick up new env vars │ │
|
|
│ │ │ │
|
|
│ └────────────────────────────────────────────────────────────────────────┘ │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ ┌────────────────────────────────────────────────────────────────────────┐ │
|
|
│ │ APP CODE │ │
|
|
│ │ │ │
|
|
│ │ # Python/Flask example │ │
|
|
│ │ import os │ │
|
|
│ │ import mysql.connector │ │
|
|
│ │ │ │
|
|
│ │ db = mysql.connector.connect( │ │
|
|
│ │ host=os.environ['DB_HOST'], # localhost │ │
|
|
│ │ port=os.environ['DB_PORT'], # 3306 │ │
|
|
│ │ database=os.environ['DB_NAME'], # my_app_db │ │
|
|
│ │ user=os.environ['DB_USER'], # app_user │ │
|
|
│ │ password=os.environ['DB_PASSWORD'] │ │
|
|
│ │ ) │ │
|
|
│ │ │ │
|
|
│ │ # Or use the URL directly: │ │
|
|
│ │ # SQLAlchemy: create_engine(os.environ['DATABASE_URL']) │ │
|
|
│ │ │ │
|
|
│ └────────────────────────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
└─────────────────────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## File Paths
|
|
|
|
### Where Everything Lives
|
|
|
|
```
|
|
SERVER FILESYSTEM
|
|
─────────────────────────────────────────────────────────────────────────────────
|
|
|
|
/var/serverkit/ # ServerKit data root
|
|
├── apps/ # All deployed applications
|
|
│ ├── my-blog/
|
|
│ │ ├── docker-compose.yml # Generated from template
|
|
│ │ ├── .env # Environment variables
|
|
│ │ └── data/ # Persistent volumes
|
|
│ ├── flask-api/
|
|
│ │ ├── docker-compose.yml
|
|
│ │ └── app/ # Application code
|
|
│ └── ...
|
|
│
|
|
├── backups/ # Database backups
|
|
│ ├── mysql/
|
|
│ └── postgres/
|
|
│
|
|
└── ssl/ # SSL certificates (if not using certbot)
|
|
|
|
/etc/serverkit/ # ServerKit configuration
|
|
├── templates/ # Template library (YAML files)
|
|
│ ├── wordpress.yaml
|
|
│ ├── flask-hello-world.yaml
|
|
│ ├── grafana.yaml
|
|
│ └── ...
|
|
└── config.yaml # Main config
|
|
|
|
/etc/nginx/ # Nginx configuration
|
|
├── sites-available/ # All site configs
|
|
│ ├── my-blog # Generated by ServerKit
|
|
│ ├── flask-api
|
|
│ └── default
|
|
├── sites-enabled/ # Enabled sites (symlinks)
|
|
│ ├── my-blog -> ../sites-available/my-blog
|
|
│ └── flask-api -> ../sites-available/flask-api
|
|
└── nginx.conf # Main nginx config
|
|
|
|
/var/log/nginx/ # Nginx logs (per-app)
|
|
├── my-blog.access.log
|
|
├── my-blog.error.log
|
|
├── flask-api.access.log
|
|
└── flask-api.error.log
|
|
|
|
/var/lib/mysql/ # MySQL data
|
|
/var/lib/postgresql/ # PostgreSQL data
|
|
```
|
|
|
|
---
|
|
|
|
## Component Diagram
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────────────────────┐
|
|
│ SERVERKIT │
|
|
│ │
|
|
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
|
|
│ │ FRONTEND (React) │ │
|
|
│ │ Served via Nginx :80/443 │ │
|
|
│ │ │ │
|
|
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
|
|
│ │ │Dashboard │ │ Apps │ │ Domains │ │ Docker │ │ Security │ │ │
|
|
│ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │
|
|
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
|
|
│ │ │Databases │ │Templates │ │ Firewall │ │ Cron │ │ Settings │ │ │
|
|
│ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │
|
|
│ │ │ │
|
|
│ └─────────────────────────────────┬────────────────────────────────────────┘ │
|
|
│ │ │
|
|
│ REST API + WebSocket │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
|
|
│ │ BACKEND (Flask) │ │
|
|
│ │ Port 5000 │ │
|
|
│ │ │ │
|
|
│ │ ┌─────────────────────────────────────────────────────────────────┐ │ │
|
|
│ │ │ SERVICES │ │ │
|
|
│ │ │ │ │ │
|
|
│ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │
|
|
│ │ │ │DockerService │ │ NginxService │ │TemplateServ. │ │ │ │
|
|
│ │ │ │ │ │ │ │ │ │ │ │
|
|
│ │ │ │ • compose_up │ │ • create_site│ │ • install │ │ │ │
|
|
│ │ │ │ • logs │ │ • enable_ssl │ │ • variables │ │ │ │
|
|
│ │ │ │ • stats │ │ • reload │ │ • validate │ │ │ │
|
|
│ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │ │
|
|
│ │ │ │ │ │
|
|
│ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │
|
|
│ │ │ │ DBService │ │ SSLService │ │SecurityServ. │ │ │ │
|
|
│ │ │ │ │ │ │ │ │ │ │ │
|
|
│ │ │ │ • create_db │ │ • certbot │ │ • ClamAV │ │ │ │
|
|
│ │ │ │ • users │ │ • renew │ │ • 2FA │ │ │ │
|
|
│ │ │ │ • backup │ │ • wildcard │ │ • firewall │ │ │ │
|
|
│ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │ │
|
|
│ │ │ │ │ │
|
|
│ │ └──────────────────────────────────────────────────────────────────┘ │ │
|
|
│ │ │ │
|
|
│ └─────────────────────────────────┬────────────────────────────────────────┘ │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
|
|
│ │ DATABASE (SQLite/PostgreSQL) │ │
|
|
│ │ │ │
|
|
│ │ ┌───────────┐ ┌───────────┐ ┌───────────┐ ┌───────────┐ ┌─────────┐ │ │
|
|
│ │ │ Apps │ │ Domains │ │ Users │ │ Databases │ │ Settings│ │ │
|
|
│ │ └───────────┘ └───────────┘ └───────────┘ └───────────┘ └─────────┘ │ │
|
|
│ │ │ │
|
|
│ └─────────────────────────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
└──────────────────────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## Troubleshooting
|
|
|
|
### 502 Bad Gateway
|
|
|
|
```
|
|
Problem: Nginx can't reach the container
|
|
|
|
Check:
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ 1. Is container running? │
|
|
│ docker ps | grep <app-name> │
|
|
│ │
|
|
│ 2. Is port bound to host? │
|
|
│ docker port <container-name> │
|
|
│ Expected: 5000/tcp -> 0.0.0.0:8001 │
|
|
│ │
|
|
│ 3. Is port accessible? │
|
|
│ curl -I http://127.0.0.1:8001 │
|
|
│ Expected: HTTP/1.1 200 OK │
|
|
│ │
|
|
│ 4. Does nginx config have correct port? │
|
|
│ cat /etc/nginx/sites-enabled/<app-name> │
|
|
│ Check: proxy_pass http://127.0.0.1:8001; │
|
|
│ │
|
|
│ 5. Check nginx error log: │
|
|
│ tail -50 /var/log/nginx/<app-name>.error.log │
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
### Container Won't Start
|
|
|
|
```
|
|
Problem: docker compose up fails
|
|
|
|
Check:
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ 1. Check compose logs: │
|
|
│ cd /var/serverkit/apps/<app-name> │
|
|
│ docker compose logs │
|
|
│ │
|
|
│ 2. Validate compose file: │
|
|
│ docker compose config │
|
|
│ │
|
|
│ 3. Check for port conflicts: │
|
|
│ docker ps --format "{{.Ports}}" │
|
|
│ netstat -tulpn | grep <port> │
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## See Also
|
|
|
|
- [Installation Guide](INSTALLATION.md)
|
|
- [API Reference](API.md)
|
|
- [Deployment Guide](DEPLOYMENT.md)
|