1421 lines
32 KiB
Markdown
1421 lines
32 KiB
Markdown
# IGNY8 Infrastructure & Deployment Documentation
|
|
|
|
**Version:** 1.0
|
|
**Last Updated:** 2025-01-XX
|
|
**Purpose:** Complete infrastructure and deployment documentation covering Docker setup, containers, networks, volumes, ports, environment configuration, and deployment procedures.
|
|
|
|
---
|
|
|
|
## Table of Contents
|
|
|
|
1. [Infrastructure Overview](#infrastructure-overview)
|
|
2. [Docker Architecture](#docker-architecture)
|
|
3. [Infrastructure Stack (igny8-infra)](#infrastructure-stack-igny8-infra)
|
|
4. [Application Stack (igny8-app)](#application-stack-igny8-app)
|
|
5. [Network Configuration](#network-configuration)
|
|
6. [Volume Management](#volume-management)
|
|
7. [Port Allocation](#port-allocation)
|
|
8. [Environment Variables](#environment-variables)
|
|
9. [Docker Images](#docker-images)
|
|
10. [Deployment Procedures](#deployment-procedures)
|
|
11. [Backup & Restore](#backup--restore)
|
|
12. [Monitoring & Health Checks](#monitoring--health-checks)
|
|
13. [Troubleshooting](#troubleshooting)
|
|
|
|
---
|
|
|
|
## Infrastructure Overview
|
|
|
|
The IGNY8 platform runs on a Docker-based infrastructure with two main stacks:
|
|
|
|
1. **Infrastructure Stack (`igny8-infra`)**: Shared services used by all applications
|
|
2. **Application Stack (`igny8-app`)**: IGNY8-specific application services
|
|
|
|
All containers run on a single Docker network (`igny8_net`) for inter-container communication.
|
|
|
|
### Infrastructure Components
|
|
|
|
**Infrastructure Stack Services**:
|
|
- PostgreSQL 15 (Database)
|
|
- Redis 7 (Cache & Celery Broker)
|
|
- pgAdmin 4 (Database Administration)
|
|
- FileBrowser (File Management)
|
|
- Caddy (Reverse Proxy & HTTPS)
|
|
- setup-helper (Utility Container)
|
|
|
|
**Application Stack Services**:
|
|
- igny8_backend (Django Backend with Gunicorn)
|
|
- igny8_frontend (React Frontend with Vite)
|
|
- igny8_celery_worker (Celery Worker)
|
|
- igny8_celery_beat (Celery Beat Scheduler)
|
|
|
|
**External Services**:
|
|
- Portainer (Container Management - runs separately)
|
|
|
|
---
|
|
|
|
## Docker Architecture
|
|
|
|
### Stack Organization
|
|
|
|
**Two-Stack Architecture**:
|
|
- **Infrastructure Stack**: Shared services (database, cache, reverse proxy)
|
|
- **Application Stack**: Application-specific services (backend, frontend, workers)
|
|
|
|
**Benefits**:
|
|
- Separation of concerns
|
|
- Independent scaling
|
|
- Shared infrastructure across multiple apps
|
|
- Easier maintenance and updates
|
|
|
|
### File Structure
|
|
|
|
```
|
|
/data/app/
|
|
├── docker-compose.yml # Infrastructure stack (igny8-infra)
|
|
└── igny8/
|
|
└── docker-compose.app.yml # Application stack (igny8-app)
|
|
```
|
|
|
|
### Network Architecture
|
|
|
|
**Single Network**: `igny8_net` (bridge network)
|
|
|
|
**Network Type**: External bridge network (created manually)
|
|
|
|
**All Containers**: Connected to `igny8_net` for inter-container communication
|
|
|
|
---
|
|
|
|
## Infrastructure Stack (igny8-infra)
|
|
|
|
**File**: `/data/app/docker-compose.yml`
|
|
|
|
**Stack Name**: `igny8-infra`
|
|
|
|
**Purpose**: Shared infrastructure services used by all applications
|
|
|
|
### Services
|
|
|
|
#### 1. PostgreSQL Database
|
|
|
|
**Container Name**: `igny8_postgres`
|
|
|
|
**Image**: `postgres:15`
|
|
|
|
**Purpose**: Primary database for all applications
|
|
|
|
**Configuration**:
|
|
- **User**: `igny8`
|
|
- **Password**: `igny8pass`
|
|
- **Database**: `igny8_db`
|
|
- **Port**: Internal only (no external port mapping)
|
|
- **Volume**: `pgdata:/var/lib/postgresql/data`
|
|
|
|
**Health Check**:
|
|
- Command: `pg_isready -U $POSTGRES_USER -d $POSTGRES_DB`
|
|
- Interval: 20s
|
|
- Timeout: 5s
|
|
- Retries: 5
|
|
- Start Period: 15s
|
|
|
|
**Network**: `igny8_net`
|
|
|
|
**Labels**:
|
|
- `com.docker.compose.project=igny8-infra`
|
|
- `com.docker.compose.service=postgres`
|
|
|
|
#### 2. Redis Cache & Broker
|
|
|
|
**Container Name**: `igny8_redis`
|
|
|
|
**Image**: `redis:7`
|
|
|
|
**Purpose**:
|
|
- Celery task broker
|
|
- Celery result backend
|
|
- Application caching
|
|
|
|
**Configuration**:
|
|
- **Port**: Internal only (6379, no external mapping)
|
|
- **Volume**: `redisdata:/data`
|
|
- **Command**: `redis-server --save 60 1 --loglevel warning`
|
|
|
|
**Health Check**:
|
|
- Command: `redis-cli ping | grep -q PONG`
|
|
- Interval: 20s
|
|
- Timeout: 3s
|
|
- Retries: 5
|
|
|
|
**Network**: `igny8_net`
|
|
|
|
**Labels**:
|
|
- `com.docker.compose.project=igny8-infra`
|
|
- `com.docker.compose.service=redis`
|
|
|
|
#### 3. pgAdmin
|
|
|
|
**Container Name**: `igny8_pgadmin`
|
|
|
|
**Image**: `dpage/pgadmin4`
|
|
|
|
**Purpose**: PostgreSQL database administration interface
|
|
|
|
**Configuration**:
|
|
- **Port**: `5050:80` (external:internal)
|
|
- **Email**: `admin@igny8.com`
|
|
- **Password**: `admin123`
|
|
- **Volume**: `pgadmin_data:/var/lib/pgadmin`
|
|
|
|
**Access**: `http://localhost:5050` or `http://<server-ip>:5050`
|
|
|
|
**Network**: `igny8_net`
|
|
|
|
**Labels**:
|
|
- `com.docker.compose.project=igny8-infra`
|
|
- `com.docker.compose.service=pgadmin`
|
|
|
|
#### 4. FileBrowser
|
|
|
|
**Container Name**: `igny8_filebrowser`
|
|
|
|
**Image**: `filebrowser/filebrowser:v2.25.0`
|
|
|
|
**Purpose**: Web-based file management interface
|
|
|
|
**Configuration**:
|
|
- **Port**: `8080:80` (external:internal)
|
|
- **Timezone**: `Asia/Karachi`
|
|
- **Volumes**:
|
|
- `/data:/srv` (read-write)
|
|
- `/backups:/srv/backups` (read-write)
|
|
- `filebrowser_db:/database` (database)
|
|
|
|
**Access**: `http://localhost:8080` or `http://<server-ip>:8080`
|
|
|
|
**Network**: `igny8_net`
|
|
|
|
**Labels**:
|
|
- `com.docker.compose.project=igny8-infra`
|
|
- `com.docker.compose.service=filebrowser`
|
|
|
|
#### 5. Caddy Reverse Proxy
|
|
|
|
**Container Name**: `igny8_caddy`
|
|
|
|
**Image**: `caddy:latest`
|
|
|
|
**Purpose**:
|
|
- Reverse proxy for all applications
|
|
- HTTPS/SSL termination
|
|
- Request routing
|
|
|
|
**Configuration**:
|
|
- **Ports**:
|
|
- `80:80` (HTTP)
|
|
- `443:443` (HTTPS)
|
|
- **Volumes**:
|
|
- `caddy_data:/data` (SSL certificates)
|
|
- `caddy_config:/config` (configuration)
|
|
- `/var/lib/docker/volumes/portainer_data/_data/caddy/Caddyfile:/etc/caddy/Caddyfile` (routing config)
|
|
|
|
**Caddyfile Location**: `/var/lib/docker/volumes/portainer_data/_data/caddy/Caddyfile`
|
|
|
|
**Network**: `igny8_net`
|
|
|
|
**Labels**:
|
|
- `com.docker.compose.project=igny8-infra`
|
|
- `com.docker.compose.service=caddy`
|
|
|
|
#### 6. Setup Helper
|
|
|
|
**Container Name**: `setup-helper`
|
|
|
|
**Image**: `alpine:3.20`
|
|
|
|
**Purpose**: Utility container for setup and maintenance tasks
|
|
|
|
**Configuration**:
|
|
- **Command**: `sh -c "sleep infinity"` (keeps container running)
|
|
- **Volumes**:
|
|
- `/data/backups:/backups:rw` (backup directory)
|
|
- `/scripts:/scripts:ro` (utility scripts)
|
|
|
|
**Network**: `igny8_net`
|
|
|
|
**Labels**:
|
|
- `com.docker.compose.project=igny8-infra`
|
|
- `com.docker.compose.service=setup-helper`
|
|
|
|
### Volumes
|
|
|
|
**Infrastructure Stack Volumes**:
|
|
- `pgdata`: PostgreSQL database data
|
|
- `redisdata`: Redis data
|
|
- `pgadmin_data`: pgAdmin configuration
|
|
- `filebrowser_db`: FileBrowser database
|
|
- `caddy_data`: Caddy SSL certificates and data
|
|
- `caddy_config`: Caddy configuration
|
|
|
|
### Network
|
|
|
|
**Network Name**: `igny8_net`
|
|
|
|
**Type**: External bridge network
|
|
|
|
**Creation**: Must be created manually before starting stacks
|
|
|
|
**Command**: `docker network create igny8_net --driver bridge`
|
|
|
|
---
|
|
|
|
## Application Stack (igny8-app)
|
|
|
|
**File**: `/data/app/igny8/docker-compose.app.yml`
|
|
|
|
**Stack Name**: `igny8-app`
|
|
|
|
**Purpose**: IGNY8 application-specific services
|
|
|
|
### Services
|
|
|
|
#### 1. Backend Service
|
|
|
|
**Container Name**: `igny8_backend`
|
|
|
|
**Image**: `igny8-backend:latest`
|
|
|
|
**Purpose**: Django REST API backend
|
|
|
|
**Configuration**:
|
|
- **Port**: `8011:8010` (external:internal)
|
|
- **Working Directory**: `/app`
|
|
- **Command**: `gunicorn igny8_core.wsgi:application --bind 0.0.0.0:8010 --workers 4 --timeout 120`
|
|
|
|
**Environment Variables**:
|
|
- `DB_HOST`: `postgres`
|
|
- `DB_NAME`: `igny8_db`
|
|
- `DB_USER`: `igny8`
|
|
- `DB_PASSWORD`: `igny8pass`
|
|
- `REDIS_HOST`: `redis`
|
|
- `REDIS_PORT`: `6379`
|
|
- `USE_SECURE_COOKIES`: `True`
|
|
- `USE_SECURE_PROXY_HEADER`: `True`
|
|
- `DEBUG`: `False`
|
|
- `SECRET_KEY`: (should be set via environment variable in production)
|
|
|
|
**Volumes**:
|
|
- `/data/app/igny8/backend:/app:rw` (application code)
|
|
- `/data/app/igny8:/data/app/igny8:ro` (read-only access to project root)
|
|
- `/var/run/docker.sock:/var/run/docker.sock:ro` (Docker socket for container management)
|
|
- `/data/app/logs:/app/logs:rw` (application logs)
|
|
|
|
**Health Check**:
|
|
- Command: `python -c "import urllib.request; urllib.request.urlopen('http://localhost:8010/api/v1/system/status/').read()"`
|
|
- Interval: 30s
|
|
- Timeout: 10s
|
|
- Retries: 3
|
|
- Start Period: 40s
|
|
|
|
**Network**: `igny8_net`
|
|
|
|
**Dependencies**:
|
|
- Requires `postgres` and `redis` from infrastructure stack (external services)
|
|
|
|
**Labels**:
|
|
- `com.docker.compose.project=igny8-app`
|
|
- `com.docker.compose.service=igny8_backend`
|
|
|
|
#### 2. Frontend Service
|
|
|
|
**Container Name**: `igny8_frontend`
|
|
|
|
**Image**: `igny8-frontend-dev:latest`
|
|
|
|
**Purpose**: React frontend with Vite dev server
|
|
|
|
**Configuration**:
|
|
- **Port**: `8021:5173` (external:internal)
|
|
- **Command**: `npm run dev -- --host 0.0.0.0 --port 5173`
|
|
|
|
**Environment Variables**:
|
|
- `VITE_BACKEND_URL`: `https://api.igny8.com/api`
|
|
|
|
**Volumes**:
|
|
- `/data/app/igny8/frontend:/app:rw` (application code)
|
|
|
|
**Dependencies**:
|
|
- `igny8_backend` (waits for backend to be healthy)
|
|
|
|
**Network**: `igny8_net`
|
|
|
|
**Labels**:
|
|
- `com.docker.compose.project=igny8-app`
|
|
- `com.docker.compose.service=igny8_frontend`
|
|
|
|
#### 3. Celery Worker
|
|
|
|
**Container Name**: `igny8_celery_worker`
|
|
|
|
**Image**: `igny8-backend:latest` (same as backend)
|
|
|
|
**Purpose**: Celery worker for asynchronous task processing
|
|
|
|
**Configuration**:
|
|
- **Working Directory**: `/app`
|
|
- **Command**: `celery -A igny8_core worker --loglevel=info --concurrency=4`
|
|
|
|
**Environment Variables**:
|
|
- `DB_HOST`: `postgres`
|
|
- `DB_NAME`: `igny8_db`
|
|
- `DB_USER`: `igny8`
|
|
- `DB_PASSWORD`: `igny8pass`
|
|
- `REDIS_HOST`: `redis`
|
|
- `REDIS_PORT`: `6379`
|
|
- `DEBUG`: `False`
|
|
|
|
**Volumes**:
|
|
- `/data/app/igny8/backend:/app:rw` (application code)
|
|
- `/data/app/igny8:/data/app/igny8:ro` (read-only access to project root)
|
|
- `/data/app/logs:/app/logs:rw` (application logs)
|
|
|
|
**Network**: `igny8_net`
|
|
|
|
**Dependencies**:
|
|
- Requires `postgres` and `redis` from infrastructure stack (external services)
|
|
|
|
**Labels**:
|
|
- `com.docker.compose.project=igny8-app`
|
|
- `com.docker.compose.service=igny8_celery_worker`
|
|
|
|
#### 4. Celery Beat
|
|
|
|
**Container Name**: `igny8_celery_beat`
|
|
|
|
**Image**: `igny8-backend:latest` (same as backend)
|
|
|
|
**Purpose**: Celery beat scheduler for periodic tasks
|
|
|
|
**Configuration**:
|
|
- **Working Directory**: `/app`
|
|
- **Command**: `celery -A igny8_core beat --loglevel=info`
|
|
|
|
**Environment Variables**:
|
|
- `DB_HOST`: `postgres`
|
|
- `DB_NAME`: `igny8_db`
|
|
- `DB_USER`: `igny8`
|
|
- `DB_PASSWORD`: `igny8pass`
|
|
- `REDIS_HOST`: `redis`
|
|
- `REDIS_PORT`: `6379`
|
|
- `DEBUG`: `False`
|
|
|
|
**Volumes**:
|
|
- `/data/app/igny8/backend:/app:rw` (application code)
|
|
- `/data/app/igny8:/data/app/igny8:ro` (read-only access to project root)
|
|
- `/data/app/logs:/app/logs:rw` (application logs)
|
|
|
|
**Network**: `igny8_net`
|
|
|
|
**Dependencies**:
|
|
- Requires `postgres` and `redis` from infrastructure stack (external services)
|
|
|
|
**Labels**:
|
|
- `com.docker.compose.project=igny8-app`
|
|
- `com.docker.compose.service=igny8_celery_beat`
|
|
|
|
### Network
|
|
|
|
**Network Name**: `igny8_net`
|
|
|
|
**Type**: External (uses network from infrastructure stack)
|
|
|
|
**Configuration**: `external: true`
|
|
|
|
---
|
|
|
|
## Network Configuration
|
|
|
|
### Network Details
|
|
|
|
**Network Name**: `igny8_net`
|
|
|
|
**Type**: Bridge network
|
|
|
|
**Driver**: `bridge`
|
|
|
|
**Scope**: External (shared across stacks)
|
|
|
|
### Network Creation
|
|
|
|
**Manual Creation Required**:
|
|
|
|
```bash
|
|
docker network create igny8_net --driver bridge
|
|
```
|
|
|
|
**Verification**:
|
|
|
|
```bash
|
|
docker network inspect igny8_net
|
|
```
|
|
|
|
### Container IP Addresses
|
|
|
|
**Infrastructure Stack** (172.18.0.x subnet):
|
|
- `igny8_postgres`: 172.18.0.6
|
|
- `igny8_redis`: 172.18.0.2
|
|
- `igny8_pgadmin`: 172.18.0.4
|
|
- `igny8_filebrowser`: 172.18.0.8
|
|
- `igny8_caddy`: 172.18.0.7
|
|
- `setup-helper`: 172.18.0.5
|
|
|
|
**Application Stack** (172.18.0.x subnet):
|
|
- `igny8_backend`: 172.18.0.3
|
|
- `igny8_frontend`: 172.18.0.11
|
|
- `igny8_celery_worker`: 172.18.0.9
|
|
- `igny8_celery_beat`: 172.18.0.10
|
|
|
|
**Note**: IP addresses are dynamically assigned by Docker and may vary.
|
|
|
|
### Inter-Container Communication
|
|
|
|
**Service Discovery**: Containers can communicate using service names as hostnames
|
|
|
|
**Examples**:
|
|
- Backend → PostgreSQL: `postgres:5432`
|
|
- Backend → Redis: `redis:6379`
|
|
- Frontend → Backend: `igny8_backend:8010` (internal) or `https://api.igny8.com/api` (external via Caddy)
|
|
|
|
---
|
|
|
|
## Volume Management
|
|
|
|
### Infrastructure Stack Volumes
|
|
|
|
#### pgdata
|
|
**Purpose**: PostgreSQL database data
|
|
|
|
**Location**: `/var/lib/docker/volumes/igny8-infra_pgdata/_data`
|
|
|
|
**Backup**: Critical - contains all database data
|
|
|
|
#### redisdata
|
|
**Purpose**: Redis data persistence
|
|
|
|
**Location**: `/var/lib/docker/volumes/igny8-infra_redisdata/_data`
|
|
|
|
**Backup**: Optional - cache data can be regenerated
|
|
|
|
#### pgadmin_data
|
|
**Purpose**: pgAdmin configuration and settings
|
|
|
|
**Location**: `/var/lib/docker/volumes/igny8-infra_pgadmin_data/_data`
|
|
|
|
**Backup**: Optional - configuration can be recreated
|
|
|
|
#### filebrowser_db
|
|
**Purpose**: FileBrowser database
|
|
|
|
**Location**: `/var/lib/docker/volumes/igny8-infra_filebrowser_db/_data`
|
|
|
|
**Backup**: Optional - file browser state
|
|
|
|
#### caddy_data
|
|
**Purpose**: Caddy SSL certificates and data
|
|
|
|
**Location**: `/var/lib/docker/volumes/igny8-infra_caddy_data/_data`
|
|
|
|
**Backup**: Important - contains SSL certificates
|
|
|
|
#### caddy_config
|
|
**Purpose**: Caddy configuration
|
|
|
|
**Location**: `/var/lib/docker/volumes/igny8-infra_caddy_config/_data`
|
|
|
|
**Backup**: Important - contains Caddy configuration
|
|
|
|
### Host Volume Mounts
|
|
|
|
#### Application Code
|
|
**Path**: `/data/app/igny8/backend:/app:rw`
|
|
|
|
**Purpose**: Backend application code (read-write for development)
|
|
|
|
#### Application Root
|
|
**Path**: `/data/app/igny8:/data/app/igny8:ro`
|
|
|
|
**Purpose**: Read-only access to project root
|
|
|
|
#### Logs
|
|
**Path**: `/data/app/logs:/app/logs:rw`
|
|
|
|
**Purpose**: Application logs directory
|
|
|
|
#### Docker Socket
|
|
**Path**: `/var/run/docker.sock:/var/run/docker.sock:ro`
|
|
|
|
**Purpose**: Read-only access to Docker socket for container management
|
|
|
|
#### FileBrowser Data
|
|
**Path**: `/data:/srv` (read-write)
|
|
|
|
**Purpose**: FileBrowser access to host filesystem
|
|
|
|
#### FileBrowser Backups
|
|
**Path**: `/backups:/srv/backups` (read-write)
|
|
|
|
**Purpose**: FileBrowser access to backup directory
|
|
|
|
#### Setup Helper Scripts
|
|
**Path**: `/scripts:/scripts:ro`
|
|
|
|
**Purpose**: Read-only access to utility scripts
|
|
|
|
#### Setup Helper Backups
|
|
**Path**: `/data/backups:/backups:rw`
|
|
|
|
**Purpose**: Backup directory access
|
|
|
|
### Volume Backup
|
|
|
|
**Docker Volumes**:
|
|
```bash
|
|
# Backup all Docker volumes
|
|
tar -czf docker-volumes-backup-$(date +%Y%m%d).tar.gz \
|
|
-C /var/lib/docker volumes/
|
|
```
|
|
|
|
**Host Mounts**:
|
|
```bash
|
|
# Backup application code
|
|
tar -czf app-backup-$(date +%Y%m%d).tar.gz -C /data app/
|
|
|
|
# Backup logs
|
|
tar -czf logs-backup-$(date +%Y%m%d).tar.gz -C /data logs/
|
|
```
|
|
|
|
---
|
|
|
|
## Port Allocation
|
|
|
|
### External Ports (Host → Container)
|
|
|
|
**Infrastructure Stack**:
|
|
- `5050` → pgAdmin (80)
|
|
- `8080` → FileBrowser (80)
|
|
- `80` → Caddy (80) - HTTP
|
|
- `443` → Caddy (443) - HTTPS
|
|
|
|
**Application Stack**:
|
|
- `8011` → Backend (8010)
|
|
- `8021` → Frontend (5173)
|
|
|
|
**Portainer** (External):
|
|
- `8000` → Portainer (8000) - HTTP
|
|
- `9443` → Portainer (9443) - HTTPS
|
|
|
|
### Internal Ports (Container → Container)
|
|
|
|
**Standardized Internal Ports**:
|
|
- Backend: `8010` (Gunicorn)
|
|
- Frontend Dev: `5173` (Vite dev server)
|
|
- Frontend Prod: `8020` (Caddy)
|
|
- PostgreSQL: `5432`
|
|
- Redis: `6379`
|
|
- pgAdmin: `80`
|
|
- FileBrowser: `80`
|
|
- Caddy: `80` (HTTP), `443` (HTTPS)
|
|
|
|
### Port Allocation Strategy
|
|
|
|
**Pattern**:
|
|
- Backend External: `XX11` (e.g., 8011)
|
|
- Frontend External: `XX21` (e.g., 8021)
|
|
|
|
**IGNY8 Port Range**: `8000-8099`
|
|
- Backend: `8011`
|
|
- Frontend: `8021`
|
|
|
|
---
|
|
|
|
## Environment Variables
|
|
|
|
### Backend Environment Variables
|
|
|
|
**Database Configuration**:
|
|
- `DB_HOST`: PostgreSQL hostname (default: `postgres`)
|
|
- `DB_NAME`: Database name (default: `igny8_db`)
|
|
- `DB_USER`: Database user (default: `igny8`)
|
|
- `DB_PASSWORD`: Database password (default: `igny8pass`)
|
|
- `DB_PORT`: Database port (default: `5432`)
|
|
|
|
**Redis Configuration**:
|
|
- `REDIS_HOST`: Redis hostname (default: `redis`)
|
|
- `REDIS_PORT`: Redis port (default: `6379`)
|
|
|
|
**Django Configuration**:
|
|
- `SECRET_KEY`: Django secret key (REQUIRED in production)
|
|
- `DEBUG`: Debug mode (default: `False`)
|
|
- `ALLOWED_HOSTS`: Comma-separated list of allowed hosts
|
|
- `DJANGO_SETTINGS_MODULE`: Django settings module (default: `igny8_core.settings`)
|
|
|
|
**Security Settings**:
|
|
- `USE_SECURE_COOKIES`: Enable secure cookies (default: `True` in production)
|
|
- `USE_SECURE_PROXY_HEADER`: Enable secure proxy header (default: `True` in production)
|
|
|
|
**JWT Configuration**:
|
|
- `JWT_SECRET_KEY`: JWT secret key (defaults to `SECRET_KEY`)
|
|
|
|
**Celery Configuration**:
|
|
- `CELERY_BROKER_URL`: Celery broker URL (default: `redis://redis:6379/0`)
|
|
- `CELERY_RESULT_BACKEND`: Celery result backend (default: `redis://redis:6379/0`)
|
|
|
|
### Frontend Environment Variables
|
|
|
|
**API Configuration**:
|
|
- `VITE_BACKEND_URL`: Backend API URL (default: `https://api.igny8.com/api`)
|
|
|
|
**Build Configuration**:
|
|
- `VITE_APP_NAME`: Application name
|
|
|
|
### Infrastructure Environment Variables
|
|
|
|
**PostgreSQL**:
|
|
- `POSTGRES_USER`: Database user (default: `igny8`)
|
|
- `POSTGRES_PASSWORD`: Database password (default: `igny8pass`)
|
|
- `POSTGRES_DB`: Database name (default: `igny8_db`)
|
|
|
|
**pgAdmin**:
|
|
- `PGADMIN_DEFAULT_EMAIL`: Admin email (default: `admin@igny8.com`)
|
|
- `PGADMIN_DEFAULT_PASSWORD`: Admin password (default: `admin123`)
|
|
|
|
**FileBrowser**:
|
|
- `TZ`: Timezone (default: `Asia/Karachi`)
|
|
|
|
---
|
|
|
|
## Docker Images
|
|
|
|
### Image Building
|
|
|
|
**Backend Image**:
|
|
```bash
|
|
cd /data/app/igny8/backend
|
|
docker build -t igny8-backend:latest -f Dockerfile .
|
|
```
|
|
|
|
**Frontend Dev Image**:
|
|
```bash
|
|
cd /data/app/igny8/frontend
|
|
docker build -t igny8-frontend-dev:latest -f Dockerfile.dev .
|
|
```
|
|
|
|
**Frontend Production Image**:
|
|
```bash
|
|
cd /data/app/igny8/frontend
|
|
docker build -t igny8-frontend:latest -f Dockerfile .
|
|
```
|
|
|
|
### Image Details
|
|
|
|
#### igny8-backend:latest
|
|
**Base Image**: `python:3.11-slim`
|
|
|
|
**Dependencies**:
|
|
- System: `gcc`, `libpq-dev`, `curl`, `git`
|
|
- Python: See `requirements.txt`
|
|
|
|
**Exposed Port**: `8010`
|
|
|
|
**Default Command**: `gunicorn igny8_core.wsgi:application --bind 0.0.0.0:8010`
|
|
|
|
**Build Context**: `/data/app/igny8/backend`
|
|
|
|
#### igny8-frontend-dev:latest
|
|
**Base Image**: `node:18-alpine`
|
|
|
|
**Dependencies**: See `package.json`
|
|
|
|
**Exposed Port**: `5173`
|
|
|
|
**Default Command**: `npm run dev -- --host 0.0.0.0 --port 5173`
|
|
|
|
**Build Context**: `/data/app/igny8/frontend`
|
|
|
|
#### igny8-frontend:latest
|
|
**Base Image**: Multi-stage build
|
|
- Stage 1: `node:18-alpine` (builder)
|
|
- Stage 2: `caddy:latest` (server)
|
|
|
|
**Exposed Port**: `8020`
|
|
|
|
**Default Command**: `caddy run --config /etc/caddy/Caddyfile`
|
|
|
|
**Build Context**: `/data/app/igny8/frontend`
|
|
|
|
### External Images
|
|
|
|
**Infrastructure Images**:
|
|
- `postgres:15` - PostgreSQL database
|
|
- `redis:7` - Redis cache and broker
|
|
- `dpage/pgadmin4` - pgAdmin interface
|
|
- `filebrowser/filebrowser:v2.25.0` - FileBrowser
|
|
- `caddy:latest` - Caddy reverse proxy
|
|
- `alpine:3.20` - Setup helper utility
|
|
|
|
**Management Images**:
|
|
- `portainer/portainer-ce:latest` - Portainer container management
|
|
|
|
---
|
|
|
|
## Deployment Procedures
|
|
|
|
### Initial Setup
|
|
|
|
#### 1. Prerequisites
|
|
|
|
**System Requirements**:
|
|
- Docker 20.10+
|
|
- Docker Compose 2.0+
|
|
- Sufficient disk space (minimum 20GB)
|
|
- Network access for pulling images
|
|
|
|
**Directory Structure**:
|
|
```bash
|
|
/data/app/
|
|
├── docker-compose.yml # Infrastructure stack
|
|
└── igny8/
|
|
├── docker-compose.app.yml # Application stack
|
|
├── backend/
|
|
└── frontend/
|
|
```
|
|
|
|
#### 2. Create Docker Network
|
|
|
|
```bash
|
|
docker network create igny8_net --driver bridge
|
|
```
|
|
|
|
#### 3. Build Docker Images
|
|
|
|
**Backend Image**:
|
|
```bash
|
|
cd /data/app/igny8/backend
|
|
docker build -t igny8-backend:latest -f Dockerfile .
|
|
```
|
|
|
|
**Frontend Dev Image**:
|
|
```bash
|
|
cd /data/app/igny8/frontend
|
|
docker build -t igny8-frontend-dev:latest -f Dockerfile.dev .
|
|
```
|
|
|
|
#### 4. Start Infrastructure Stack
|
|
|
|
```bash
|
|
cd /data/app
|
|
docker compose -f docker-compose.yml -p igny8-infra up -d
|
|
```
|
|
|
|
**Verify**:
|
|
```bash
|
|
docker ps --filter "label=com.docker.compose.project=igny8-infra"
|
|
```
|
|
|
|
#### 5. Start Application Stack
|
|
|
|
```bash
|
|
cd /data/app/igny8
|
|
docker compose -f docker-compose.app.yml -p igny8-app up -d
|
|
```
|
|
|
|
**Verify**:
|
|
```bash
|
|
docker ps --filter "label=com.docker.compose.project=igny8-app"
|
|
```
|
|
|
|
### Database Initialization
|
|
|
|
#### 1. Run Migrations
|
|
|
|
```bash
|
|
docker exec -it igny8_backend python manage.py migrate
|
|
```
|
|
|
|
#### 2. Create Superuser
|
|
|
|
```bash
|
|
docker exec -it igny8_backend python manage.py createsuperuser
|
|
```
|
|
|
|
#### 3. Load Initial Data (Optional)
|
|
|
|
```bash
|
|
docker exec -it igny8_backend python manage.py loaddata initial_data.json
|
|
```
|
|
|
|
### Caddy Configuration
|
|
|
|
**Caddyfile Location**: `/var/lib/docker/volumes/portainer_data/_data/caddy/Caddyfile`
|
|
|
|
**Configuration**: Managed via Portainer or manually
|
|
|
|
**Example Configuration**:
|
|
```
|
|
api.igny8.com {
|
|
reverse_proxy igny8_backend:8010
|
|
}
|
|
|
|
app.igny8.com {
|
|
reverse_proxy igny8_frontend:5173
|
|
}
|
|
```
|
|
|
|
**Reload Caddy**:
|
|
```bash
|
|
docker exec igny8_caddy caddy reload --config /etc/caddy/Caddyfile
|
|
```
|
|
|
|
### Production Deployment
|
|
|
|
#### 1. Set Environment Variables
|
|
|
|
**Create `.env` file or set via docker-compose**:
|
|
```bash
|
|
SECRET_KEY=<generate-secure-key>
|
|
JWT_SECRET_KEY=<generate-secure-key>
|
|
DEBUG=False
|
|
USE_SECURE_COOKIES=True
|
|
USE_SECURE_PROXY_HEADER=True
|
|
```
|
|
|
|
#### 2. Update Caddyfile
|
|
|
|
Configure domain names and SSL certificates in Caddyfile
|
|
|
|
#### 3. Build Production Images
|
|
|
|
**Backend** (already built):
|
|
```bash
|
|
cd /data/app/igny8/backend
|
|
docker build -t igny8-backend:latest -f Dockerfile .
|
|
```
|
|
|
|
**Frontend Production**:
|
|
```bash
|
|
cd /data/app/igny8/frontend
|
|
docker build -t igny8-frontend:latest -f Dockerfile .
|
|
```
|
|
|
|
#### 4. Update docker-compose.app.yml
|
|
|
|
Change frontend image from `igny8-frontend-dev:latest` to `igny8-frontend:latest`
|
|
|
|
#### 5. Restart Stacks
|
|
|
|
```bash
|
|
# Restart application stack
|
|
cd /data/app/igny8
|
|
docker compose -f docker-compose.app.yml -p igny8-app restart
|
|
```
|
|
|
|
### Updates and Maintenance
|
|
|
|
#### Update Application Code
|
|
|
|
**Backend**:
|
|
```bash
|
|
cd /data/app/igny8/backend
|
|
git pull # or copy new files
|
|
docker compose -f docker-compose.app.yml -p igny8-app restart igny8_backend
|
|
```
|
|
|
|
**Frontend**:
|
|
```bash
|
|
cd /data/app/igny8/frontend
|
|
git pull # or copy new files
|
|
docker compose -f docker-compose.app.yml -p igny8-app restart igny8_frontend
|
|
```
|
|
|
|
#### Rebuild Images
|
|
|
|
**Backend**:
|
|
```bash
|
|
cd /data/app/igny8/backend
|
|
docker build -t igny8-backend:latest -f Dockerfile .
|
|
docker compose -f docker-compose.app.yml -p igny8-app up -d --force-recreate igny8_backend igny8_celery_worker igny8_celery_beat
|
|
```
|
|
|
|
**Frontend**:
|
|
```bash
|
|
cd /data/app/igny8/frontend
|
|
docker build -t igny8-frontend-dev:latest -f Dockerfile.dev .
|
|
docker compose -f docker-compose.app.yml -p igny8-app up -d --force-recreate igny8_frontend
|
|
```
|
|
|
|
#### Database Migrations
|
|
|
|
```bash
|
|
docker exec -it igny8_backend python manage.py migrate
|
|
```
|
|
|
|
#### Collect Static Files
|
|
|
|
```bash
|
|
docker exec -it igny8_backend python manage.py collectstatic --noinput
|
|
```
|
|
|
|
---
|
|
|
|
## Backup & Restore
|
|
|
|
### Backup Procedures
|
|
|
|
#### 1. Database Backup
|
|
|
|
**PostgreSQL Dump**:
|
|
```bash
|
|
docker exec igny8_postgres pg_dump -U igny8 igny8_db > backup-$(date +%Y%m%d).sql
|
|
```
|
|
|
|
**With Compression**:
|
|
```bash
|
|
docker exec igny8_postgres pg_dump -U igny8 igny8_db | gzip > backup-$(date +%Y%m%d).sql.gz
|
|
```
|
|
|
|
#### 2. Docker Volumes Backup
|
|
|
|
**All Volumes**:
|
|
```bash
|
|
tar -czf docker-volumes-backup-$(date +%Y%m%d).tar.gz \
|
|
-C /var/lib/docker volumes/
|
|
```
|
|
|
|
**Individual Volumes**:
|
|
```bash
|
|
# PostgreSQL data
|
|
tar -czf pgdata-backup-$(date +%Y%m%d).tar.gz \
|
|
-C /var/lib/docker/volumes/igny8-infra_pgdata/_data .
|
|
|
|
# Redis data
|
|
tar -czf redisdata-backup-$(date +%Y%m%d).tar.gz \
|
|
-C /var/lib/docker/volumes/igny8-infra_redisdata/_data .
|
|
|
|
# Caddy data (SSL certificates)
|
|
tar -czf caddy-data-backup-$(date +%Y%m%d).tar.gz \
|
|
-C /var/lib/docker/volumes/igny8-infra_caddy_data/_data .
|
|
```
|
|
|
|
#### 3. Application Code Backup
|
|
|
|
```bash
|
|
tar -czf app-backup-$(date +%Y%m%d).tar.gz -C /data app/
|
|
```
|
|
|
|
#### 4. Logs Backup
|
|
|
|
```bash
|
|
tar -czf logs-backup-$(date +%Y%m%d).tar.gz -C /data logs/
|
|
```
|
|
|
|
#### 5. Complete System Backup
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
BACKUP_DIR="/backups/complete-backup-$(date +%Y%m%d)"
|
|
mkdir -p "$BACKUP_DIR"
|
|
|
|
# Database
|
|
docker exec igny8_postgres pg_dump -U igny8 igny8_db | gzip > "$BACKUP_DIR/database.sql.gz"
|
|
|
|
# Docker volumes
|
|
tar -czf "$BACKUP_DIR/docker-volumes.tar.gz" -C /var/lib/docker volumes/
|
|
|
|
# Application code
|
|
tar -czf "$BACKUP_DIR/app.tar.gz" -C /data app/
|
|
|
|
# Logs
|
|
tar -czf "$BACKUP_DIR/logs.tar.gz" -C /data logs/
|
|
|
|
# Caddyfile
|
|
cp /var/lib/docker/volumes/portainer_data/_data/caddy/Caddyfile "$BACKUP_DIR/Caddyfile"
|
|
```
|
|
|
|
### Restore Procedures
|
|
|
|
#### 1. Database Restore
|
|
|
|
**From SQL Dump**:
|
|
```bash
|
|
docker exec -i igny8_postgres psql -U igny8 igny8_db < backup-YYYYMMDD.sql
|
|
```
|
|
|
|
**From Compressed Dump**:
|
|
```bash
|
|
gunzip -c backup-YYYYMMDD.sql.gz | docker exec -i igny8_postgres psql -U igny8 igny8_db
|
|
```
|
|
|
|
#### 2. Docker Volumes Restore
|
|
|
|
**Stop Services**:
|
|
```bash
|
|
docker compose -f docker-compose.app.yml -p igny8-app down
|
|
docker compose -f docker-compose.yml -p igny8-infra down
|
|
```
|
|
|
|
**Restore Volumes**:
|
|
```bash
|
|
# Extract volume backups
|
|
tar -xzf docker-volumes-backup-YYYYMMDD.tar.gz -C /var/lib/docker/
|
|
|
|
# Or restore individual volumes
|
|
tar -xzf pgdata-backup-YYYYMMDD.tar.gz -C /var/lib/docker/volumes/igny8-infra_pgdata/_data/
|
|
```
|
|
|
|
**Start Services**:
|
|
```bash
|
|
docker compose -f docker-compose.yml -p igny8-infra up -d
|
|
docker compose -f docker-compose.app.yml -p igny8-app up -d
|
|
```
|
|
|
|
#### 3. Application Code Restore
|
|
|
|
```bash
|
|
tar -xzf app-backup-YYYYMMDD.tar.gz -C /data/
|
|
```
|
|
|
|
#### 4. Complete System Restore
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
BACKUP_DIR="/backups/complete-backup-YYYYMMDD"
|
|
|
|
# Restore database
|
|
gunzip -c "$BACKUP_DIR/database.sql.gz" | docker exec -i igny8_postgres psql -U igny8 igny8_db
|
|
|
|
# Restore volumes
|
|
tar -xzf "$BACKUP_DIR/docker-volumes.tar.gz" -C /var/lib/docker/
|
|
|
|
# Restore application code
|
|
tar -xzf "$BACKUP_DIR/app.tar.gz" -C /data/
|
|
|
|
# Restore Caddyfile
|
|
cp "$BACKUP_DIR/Caddyfile" /var/lib/docker/volumes/portainer_data/_data/caddy/Caddyfile
|
|
|
|
# Restart services
|
|
docker compose -f docker-compose.yml -p igny8-infra restart caddy
|
|
docker compose -f docker-compose.app.yml -p igny8-app restart
|
|
```
|
|
|
|
---
|
|
|
|
## Monitoring & Health Checks
|
|
|
|
### Health Check Endpoints
|
|
|
|
#### Backend Health Check
|
|
|
|
**Endpoint**: `http://localhost:8011/api/v1/system/status/`
|
|
|
|
**Method**: `GET`
|
|
|
|
**Expected Response**: HTTP 200
|
|
|
|
**Implementation**: Django view that checks database connectivity
|
|
|
|
#### Frontend Health Check
|
|
|
|
**Endpoint**: `http://localhost:8021/`
|
|
|
|
**Method**: `GET`
|
|
|
|
**Expected Response**: HTTP 200 (HTML page)
|
|
|
|
### Container Health Checks
|
|
|
|
#### PostgreSQL
|
|
|
|
**Check**: `pg_isready -U igny8 -d igny8_db`
|
|
|
|
**Interval**: 20s
|
|
|
|
**Timeout**: 5s
|
|
|
|
**Retries**: 5
|
|
|
|
#### Redis
|
|
|
|
**Check**: `redis-cli ping | grep -q PONG`
|
|
|
|
**Interval**: 20s
|
|
|
|
**Timeout**: 3s
|
|
|
|
**Retries**: 5
|
|
|
|
#### Backend
|
|
|
|
**Check**: `python -c "import urllib.request; urllib.request.urlopen('http://localhost:8010/api/v1/system/status/').read()"`
|
|
|
|
**Interval**: 30s
|
|
|
|
**Timeout**: 10s
|
|
|
|
**Retries**: 3
|
|
|
|
**Start Period**: 40s
|
|
|
|
### Monitoring Script
|
|
|
|
**File**: `/data/app/igny8/check-status.sh`
|
|
|
|
**Purpose**: Quick status check for all stacks and containers
|
|
|
|
**Usage**:
|
|
```bash
|
|
cd /data/app/igny8
|
|
./check-status.sh
|
|
```
|
|
|
|
**Output**:
|
|
- Container status for both stacks
|
|
- Network connectivity
|
|
- Service health checks (Backend API, Frontend, PostgreSQL, Redis)
|
|
- All IGNY8 containers list
|
|
|
|
### Log Monitoring
|
|
|
|
#### View Container Logs
|
|
|
|
**Backend**:
|
|
```bash
|
|
docker logs -f igny8_backend
|
|
```
|
|
|
|
**Frontend**:
|
|
```bash
|
|
docker logs -f igny8_frontend
|
|
```
|
|
|
|
**Celery Worker**:
|
|
```bash
|
|
docker logs -f igny8_celery_worker
|
|
```
|
|
|
|
**Celery Beat**:
|
|
```bash
|
|
docker logs -f igny8_celery_beat
|
|
```
|
|
|
|
**PostgreSQL**:
|
|
```bash
|
|
docker logs -f igny8_postgres
|
|
```
|
|
|
|
**Redis**:
|
|
```bash
|
|
docker logs -f igny8_redis
|
|
```
|
|
|
|
#### Application Logs
|
|
|
|
**Location**: `/data/app/logs/`
|
|
|
|
**Backend Logs**: `/data/app/logs/backend/`
|
|
|
|
**Frontend Logs**: `/data/app/logs/frontend/`
|
|
|
|
---
|
|
|
|
## Troubleshooting
|
|
|
|
### Common Issues
|
|
|
|
#### 1. Network Not Found
|
|
|
|
**Error**: `network igny8_net not found`
|
|
|
|
**Solution**:
|
|
```bash
|
|
docker network create igny8_net --driver bridge
|
|
```
|
|
|
|
#### 2. Database Connection Failed
|
|
|
|
**Error**: `could not connect to server: Connection refused`
|
|
|
|
**Check**:
|
|
```bash
|
|
# Verify PostgreSQL is running
|
|
docker ps | grep igny8_postgres
|
|
|
|
# Check PostgreSQL logs
|
|
docker logs igny8_postgres
|
|
|
|
# Test connection
|
|
docker exec igny8_postgres pg_isready -U igny8
|
|
```
|
|
|
|
**Solution**: Ensure infrastructure stack is running before application stack
|
|
|
|
#### 3. Redis Connection Failed
|
|
|
|
**Error**: `Error connecting to Redis`
|
|
|
|
**Check**:
|
|
```bash
|
|
# Verify Redis is running
|
|
docker ps | grep igny8_redis
|
|
|
|
# Check Redis logs
|
|
docker logs igny8_redis
|
|
|
|
# Test connection
|
|
docker exec igny8_redis redis-cli ping
|
|
```
|
|
|
|
**Solution**: Ensure infrastructure stack is running before application stack
|
|
|
|
#### 4. Port Already in Use
|
|
|
|
**Error**: `Bind for 0.0.0.0:8011 failed: port is already allocated`
|
|
|
|
**Check**:
|
|
```bash
|
|
# Find process using port
|
|
sudo lsof -i :8011
|
|
# or
|
|
sudo netstat -tulpn | grep 8011
|
|
```
|
|
|
|
**Solution**: Stop conflicting service or change port in docker-compose
|
|
|
|
#### 5. Container Health Check Failing
|
|
|
|
**Error**: Container shows as unhealthy
|
|
|
|
**Check**:
|
|
```bash
|
|
# View container health status
|
|
docker inspect igny8_backend | grep -A 10 Health
|
|
|
|
# View container logs
|
|
docker logs igny8_backend
|
|
|
|
# Test health check manually
|
|
docker exec igny8_backend python -c "import urllib.request; urllib.request.urlopen('http://localhost:8010/api/v1/system/status/').read()"
|
|
```
|
|
|
|
**Solution**: Check application logs, verify dependencies are running
|
|
|
|
#### 6. Celery Tasks Not Processing
|
|
|
|
**Check**:
|
|
```bash
|
|
# Verify Celery worker is running
|
|
docker ps | grep igny8_celery_worker
|
|
|
|
# Check Celery worker logs
|
|
docker logs igny8_celery_worker
|
|
|
|
# Verify Redis connection
|
|
docker exec igny8_celery_worker celery -A igny8_core inspect active
|
|
```
|
|
|
|
**Solution**: Restart Celery worker, verify Redis connectivity
|
|
|
|
#### 7. Static Files Not Loading
|
|
|
|
**Error**: 404 for static files
|
|
|
|
**Solution**:
|
|
```bash
|
|
# Collect static files
|
|
docker exec igny8_backend python manage.py collectstatic --noinput
|
|
|
|
# Restart backend
|
|
docker compose -f docker-compose.app.yml -p igny8-app restart igny8_backend
|
|
```
|
|
|
|
#### 8. CORS Errors
|
|
|
|
**Error**: CORS policy blocked request
|
|
|
|
**Check**: Verify `CORS_ALLOWED_ORIGINS` in Django settings
|
|
|
|
**Solution**: Add frontend domain to `CORS_ALLOWED_ORIGINS` in `settings.py`
|
|
|
|
### Debugging Commands
|
|
|
|
#### Container Inspection
|
|
|
|
```bash
|
|
# Inspect container configuration
|
|
docker inspect igny8_backend
|
|
|
|
# View container environment variables
|
|
docker exec igny8_backend env
|
|
|
|
# View container network configuration
|
|
docker network inspect igny8_net
|
|
```
|
|
|
|
#### Database Debugging
|
|
|
|
```bash
|
|
# Connect to PostgreSQL
|
|
docker exec -it igny8_postgres psql -U igny8 igny8_db
|
|
|
|
# List databases
|
|
docker exec igny8_postgres psql -U igny8 -l
|
|
|
|
# Check database size
|
|
docker exec igny8_postgres psql -U igny8 -c "SELECT pg_size_pretty(pg_database_size('igny8_db'));"
|
|
```
|
|
|
|
#### Redis Debugging
|
|
|
|
```bash
|
|
# Connect to Redis CLI
|
|
docker exec -it igny8_redis redis-cli
|
|
|
|
# Check Redis info
|
|
docker exec igny8_redis redis-cli INFO
|
|
|
|
# List all keys
|
|
docker exec igny8_redis redis-cli KEYS '*'
|
|
```
|
|
|
|
#### Application Debugging
|
|
|
|
```bash
|
|
# Django shell
|
|
docker exec -it igny8_backend python manage.py shell
|
|
|
|
# Check Django settings
|
|
docker exec igny8_backend python manage.py diffsettings
|
|
|
|
# View Django URLs
|
|
docker exec igny8_backend python manage.py show_urls
|
|
```
|
|
|
|
---
|
|
|
|
## Summary
|
|
|
|
The IGNY8 infrastructure is built on:
|
|
|
|
1. **Two-Stack Architecture**: Infrastructure stack (shared services) and Application stack (app-specific services)
|
|
2. **Single Network**: All containers on `igny8_net` bridge network
|
|
3. **Docker Compose**: Separate compose files for infrastructure and application
|
|
4. **Volume Management**: Docker volumes for persistent data, host mounts for code
|
|
5. **Health Checks**: Built-in health checks for critical services
|
|
6. **Port Allocation**: Standardized port allocation (8011 backend, 8021 frontend)
|
|
7. **Environment Variables**: Configuration via environment variables
|
|
8. **Backup & Restore**: Comprehensive backup procedures for database, volumes, and code
|
|
9. **Monitoring**: Health checks, logging, and status scripts
|
|
10. **Troubleshooting**: Common issues and debugging commands
|
|
|
|
This infrastructure ensures reliability, scalability, and maintainability while providing a solid foundation for the IGNY8 application.
|
|
|