# Scaling Infrastructure

Scaling your hosting infrastructure is crucial for ensuring high availability, performance, and flexibility of services. This guide covers both PteroCA panel scaling and Pterodactyl node expansion to handle growing customer bases and server counts.

## Overview

Scaling infrastructure in the PteroCA ecosystem involves two main components:

1. **PteroCA Panel** - The client-facing billing and management interface
2. **Pterodactyl Panel & Nodes** - The game server management platform

Each component has different scaling strategies and requirements. This guide provides detailed instructions for scaling from a single-server setup to a distributed, high-availability architecture.

***

## PteroCA Architecture

Understanding PteroCA's architecture is essential for effective scaling decisions.

### System Components

**Application Layer:**

* **Web Server**: Nginx serving PHP-FPM (PHP 8.2+)
* **Application**: Symfony 7 framework with Doctrine ORM
* **Background Jobs**: Symfony Messenger for async task processing

**Data Layer:**

* **Database**: MySQL 8.0 or MariaDB 10.2+ (UTF8MB4 charset)
* **Session Storage**: File-based (default) or Redis (recommended for scaling)
* **Cache**: Filesystem (default), Redis or APCu (recommended for scaling)
* **Queue**: Database-backed (default), Redis or RabbitMQ (recommended for scaling)

**Static Assets:**

* **Uploads**: User files, logos, favicons
* **Compiled Assets**: CSS, JavaScript (Bootstrap 5, EasyAdmin theme)
* **Theme Assets**: Custom theme files

### How PteroCA Works

**PteroCA** serves as a client-facing interface that interacts with your Pterodactyl panel through its API. It maintains essential metadata about purchased servers—such as specifications, billing details, and user associations—within its own database. This information is crucial for managing user accounts, billing, and service provisioning.

However, the actual server configurations, deployments, and runtime management are handled by **Pterodactyl**. All operational aspects, including server creation, resource allocation, and lifecycle management, occur within the Pterodactyl environment. PteroCA does not directly manage or store the operational data of the game servers themselves.

This separation ensures that while PteroCA manages the business and user interaction layer, Pterodactyl remains responsible for the technical management of game servers.

### Data Flow

1. **User Request** → Nginx → PHP-FPM → PteroCA Application
2. **Server Purchase** → Database (order data) → Queue (async tasks) → Pterodactyl API (server creation)
3. **Session Data** → Session Storage (files or Redis)
4. **Cache Queries** → Cache Layer (filesystem, Redis, or APCu)
5. **Background Jobs** → Queue System → Job Workers → External Services (email, API calls)

***

## Single Instance Deployment

For small to medium deployments (up to \~500 customers), a single-instance setup is sufficient.

### Recommended Specifications

**Small Deployment (50-200 customers):**

* CPU: 4 cores
* RAM: 8GB
* Disk: 50GB SSD
* Network: 100 Mbps

**Medium Deployment (200-500 customers):**

* CPU: 8 cores
* RAM: 16GB
* Disk: 100GB SSD
* Network: 1 Gbps

### Configuration for Single Instance

**Environment Variables** (`.env`):

```env
# Database
DATABASE_URL="mysql://user:password@localhost:3306/pteroca?serverVersion=8.0"

# Session Storage (file-based)
SESSION_HANDLER=files
SESSION_SAVE_PATH=/var/www/pteroca/var/sessions

# Cache (filesystem)
CACHE_ADAPTER=filesystem

# Queue (database-backed)
MESSENGER_TRANSPORT_DSN=doctrine://default
```

**PHP-FPM Configuration** (`www.conf`):

```ini
pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 35
pm.max_requests = 1000
```

**OPcache Settings** (`php.ini`):

```ini
opcache.enable=1
opcache.memory_consumption=128M
opcache.interned_strings_buffer=8M
opcache.max_accelerated_files=10000
opcache.revalidate_freq=2
opcache.fast_shutdown=1
```

**Nginx Configuration**:

```nginx
client_max_body_size 128M;
fastcgi_buffers 16 16k;
fastcgi_buffer_size 32k;
fastcgi_read_timeout 300s;
```

### Maintenance Tasks

**Daily:**

* Monitor disk space (logs, cache, uploads)
* Check error logs for issues
* Verify backup completion

**Weekly:**

* Review application performance
* Check database size and optimization needs
* Update software packages

**Monthly:**

* Analyze growth trends
* Plan capacity increases
* Review security patches

***

## Horizontal Scaling (Multiple Instances)

For larger deployments (500+ customers) or high-availability requirements, distribute the load across multiple instances.

### Infrastructure Requirements

**Minimum HA Setup:**

* **Web Instances**: 2+ application servers
* **Load Balancer**: 1 (HAProxy or Nginx)
* **Database**: 1 master + 1 replica (read scaling)
* **Redis**: 1 instance (sessions, cache, queues)
* **Shared Storage**: NFS or S3-compatible for uploads

**Recommended HA Setup:**

* **Web Instances**: 3+ application servers
* **Load Balancer**: 2 (active-passive or active-active)
* **Database**: 1 master + 2 replicas
* **Redis**: 3 instances (Sentinel or Cluster mode)
* **Shared Storage**: Distributed filesystem or object storage

### Required Configuration Changes

#### 1. Session Storage (Redis)

**Install Redis:**

```bash
apt-get install redis-server
systemctl enable redis-server
```

**PteroCA Configuration** (`config/packages/framework.yaml`):

```yaml
framework:
    session:
        handler_id: Symfony\Component\HttpFoundation\Session\Storage\Handler\RedisSessionHandler
        save_path: 'redis://redis:6379'
```

**Environment Variables**:

```env
REDIS_URL=redis://redis-host:6379
SESSION_HANDLER=redis
SESSION_SAVE_PATH=tcp://redis-host:6379
```

#### 2. Cache Backend (Redis)

**Cache Configuration** (`config/packages/cache.yaml`):

```yaml
framework:
    cache:
        app: cache.adapter.redis
        default_redis_provider: '%env(REDIS_URL)%'
        pools:
            doctrine.result_cache_pool:
                adapter: cache.adapter.redis
            doctrine.system_cache_pool:
                adapter: cache.adapter.redis
```

**Environment Variables**:

```env
CACHE_ADAPTER=redis
CACHE_REDIS_URL=redis://redis-host:6379
```

#### 3. Message Queue (Redis)

**Messenger Configuration** (`config/packages/messenger.yaml`):

```yaml
framework:
    messenger:
        transports:
            async:
                dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
                retry_strategy:
                    max_retries: 3
                    delay: 1000
                    multiplier: 2
                    max_delay: 10000
```

**Environment Variables**:

```env
MESSENGER_TRANSPORT_DSN=redis://redis-host:6379/messages
```

#### 4. Shared File Storage

**Option A: NFS Mount**

```bash
# On storage server
apt-get install nfs-kernel-server
mkdir -p /exports/pteroca/uploads
echo "/exports/pteroca/uploads *(rw,sync,no_subtree_check)" >> /etc/exports
exportfs -a

# On each web instance
apt-get install nfs-common
mount -t nfs storage-server:/exports/pteroca/uploads /var/www/pteroca/public/uploads
```

**Option B: S3-Compatible Storage** Install S3 adapter (via Composer package) and configure Vich Uploader to use S3 backend.

#### 5. Load Balancer Configuration

**HAProxy Example** (`haproxy.cfg`):

```
frontend pteroca_frontend
    bind *:80
    bind *:443 ssl crt /etc/ssl/pteroca.pem
    default_backend pteroca_backend

backend pteroca_backend
    balance roundrobin
    option httpchk GET /health
    cookie SERVERID insert indirect nocache
    server web1 web1.internal:80 check cookie web1
    server web2 web2.internal:80 check cookie web2
    server web3 web3.internal:80 check cookie web3
```

**Nginx Load Balancer Example**:

```nginx
upstream pteroca_backend {
    least_conn;
    server web1.internal:80 max_fails=3 fail_timeout=30s;
    server web2.internal:80 max_fails=3 fail_timeout=30s;
    server web3.internal:80 max_fails=3 fail_timeout=30s;
}

server {
    listen 80;
    server_name pteroca.example.com;

    location / {
        proxy_pass http://pteroca_backend;
        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;
    }
}
```

### Deployment Workflow

1. **Deploy Code to All Instances:**

```bash
# On each web instance
cd /var/www/pteroca
git pull origin main
composer install --no-dev --optimize-autoloader
php bin/console cache:clear --env=prod
php bin/console assets:install public --symlink
```

2. **Run Migrations (One Instance Only):**

```bash
# On designated migration instance
php bin/console doctrine:migrations:migrate --no-interaction
```

3. **Restart Services:**

```bash
# On each web instance
systemctl restart php8.2-fpm
systemctl reload nginx
```

4. **Verify Health:**

```bash
curl http://web1.internal/health
curl http://web2.internal/health
curl http://web3.internal/health
```

***

## Database Scaling

### Read Replicas

For read-heavy workloads, configure MySQL read replicas.

**Master-Slave Replication Setup:**

**On Master** (`my.cnf`):

```ini
[mysqld]
server-id=1
log_bin=/var/log/mysql/mysql-bin.log
binlog_do_db=pteroca
```

**On Replica** (`my.cnf`):

```ini
[mysqld]
server-id=2
relay-log=/var/log/mysql/relay-bin
read_only=1
```

**Doctrine Configuration** (`config/packages/doctrine.yaml`):

```yaml
doctrine:
    dbal:
        connections:
            default:
                driver: pdo_mysql
                url: '%env(DATABASE_URL)%'
                slaves:
                    slave1:
                        url: '%env(DATABASE_REPLICA1_URL)%'
                    slave2:
                        url: '%env(DATABASE_REPLICA2_URL)%'
```

### Connection Pooling

Use ProxySQL or MaxScale for connection pooling:

**ProxySQL Configuration:**

```sql
INSERT INTO mysql_servers (hostgroup_id, hostname, port) VALUES
(0, 'mysql-master', 3306),
(1, 'mysql-replica1', 3306),
(1, 'mysql-replica2', 3306);

INSERT INTO mysql_query_rules (rule_id, active, match_pattern, destination_hostgroup, apply)
VALUES
(1, 1, '^SELECT.*FOR UPDATE', 0, 1),
(2, 1, '^SELECT', 1, 1);
```

**PteroCA Connection:**

```env
DATABASE_URL="mysql://user:password@proxysql-host:6033/pteroca"
```

### Database Optimization

**Regular Maintenance:**

```sql
-- Optimize tables weekly
OPTIMIZE TABLE server, user, product, payment;

-- Analyze tables for query optimization
ANALYZE TABLE server, user, product, payment;

-- Check for slow queries
SELECT * FROM mysql.slow_log ORDER BY query_time DESC LIMIT 10;
```

**Index Optimization:**

```sql
-- Add indexes for common queries
CREATE INDEX idx_server_user ON server(user_id);
CREATE INDEX idx_server_status ON server(status);
CREATE INDEX idx_payment_user_date ON payment(user_id, created_at);
```

***

## Caching Strategies

### Redis Caching

**Redis Installation:**

```bash
apt-get install redis-server
```

**Redis Configuration** (`/etc/redis/redis.conf`):

```ini
maxmemory 2gb
maxmemory-policy allkeys-lru
save 900 1
save 300 10
save 60 10000
```

**PteroCA Cache Configuration:**

```yaml
framework:
    cache:
        app: cache.adapter.redis
        default_redis_provider: 'redis://localhost:6379'
        pools:
            cache.app:
                adapter: cache.adapter.redis
                default_lifetime: 3600
```

### APCu (Alternative for Single Instance)

**Installation:**

```bash
apt-get install php8.2-apcu
```

**Configuration** (`php.ini`):

```ini
apc.enabled=1
apc.shm_size=128M
apc.ttl=7200
apc.enable_cli=1
```

**PteroCA Configuration:**

```yaml
framework:
    cache:
        app: cache.adapter.apcu
```

### Cache Invalidation

**Manual Cache Clear:**

```bash
php bin/console cache:clear --env=prod
php bin/console cache:warmup --env=prod
```

**Programmatic Cache Invalidation:** Settings are cached for 24 hours via `SettingService`. After updating settings, cache is automatically invalidated.

***

## Performance Optimization

### PHP Configuration

**Production Settings** (`php.ini`):

```ini
memory_limit = 512M
max_execution_time = 300
max_input_time = 300
post_max_size = 128M
upload_max_filesize = 128M

; OPcache
opcache.enable=1
opcache.enable_cli=1
opcache.memory_consumption=256M
opcache.interned_strings_buffer=16M
opcache.max_accelerated_files=20000
opcache.revalidate_freq=2
opcache.fast_shutdown=1
opcache.validate_timestamps=1

; Realpath cache
realpath_cache_size=4096K
realpath_cache_ttl=600
```

### PHP-FPM Tuning

**For 8GB RAM Server** (`www.conf`):

```ini
pm = dynamic
pm.max_children = 50
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 35
pm.max_requests = 1000
pm.process_idle_timeout = 10s
request_terminate_timeout = 300
```

**For 16GB RAM Server:**

```ini
pm = dynamic
pm.max_children = 100
pm.start_servers = 20
pm.min_spare_servers = 10
pm.max_spare_servers = 50
pm.max_requests = 500
```

**Calculate max\_children:**

```
max_children = (Total RAM - System RAM - MySQL RAM) / Average PHP Process Size
Example: (8GB - 1GB - 2GB) / 50MB = 100 processes
```

### Nginx Optimization

```nginx
worker_processes auto;
worker_connections 2048;
keepalive_timeout 65;
keepalive_requests 100;

gzip on;
gzip_comp_level 5;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml;

client_body_buffer_size 128k;
client_max_body_size 128M;

fastcgi_buffering on;
fastcgi_buffer_size 32k;
fastcgi_buffers 16 16k;
fastcgi_read_timeout 300;
fastcgi_send_timeout 300;
```

### Database Performance

**MySQL Configuration** (`my.cnf`):

```ini
[mysqld]
innodb_buffer_pool_size = 4G  # 50-70% of available RAM
innodb_log_file_size = 512M
innodb_flush_log_at_trx_commit = 2
innodb_flush_method = O_DIRECT
query_cache_size = 0  # Disabled in MySQL 8.0+
query_cache_type = 0
max_connections = 200
```

***

## Scaling Pterodactyl

### Single Panel Architecture

It's recommended to maintain a **single instance** of the Pterodactyl panel that manages all nodes. This architecture simplifies management and allows centralized monitoring of all servers.

**Why Single Panel?**

* Centralized user management
* Unified server inventory
* Simplified API integration with PteroCA
* Consistent configuration across all nodes

### Adding Nodes

To increase server capacity, add new nodes to the existing panel instance.

#### Step 1: Create a Location

In the Pterodactyl panel:

1. Navigate to **Admin → Locations**
2. Click **Create New**
3. Configure location:
   * **Short Code**: `us-east` (identifier)
   * **Description**: "US East Coast Data Center"
4. Click **Create Location**

**Location Purposes:**

* Group nodes by physical location
* Geographic distribution for latency reduction
* Organizational separation (production vs. staging)

#### Step 2: Add a New Node

1. Navigate to **Admin → Nodes**
2. Click **Create New**
3. Fill in required information:

**Basic Settings:**

* **Name**: `us-east-node-01`
* **Description**: "Production node in Virginia"
* **Location**: Select created location
* **FQDN**: `us-east-node-01.example.com` (must resolve via DNS)
* **Communicate Over SSL**: Recommended (Yes)
* **Behind Proxy**: Yes (if using CDN/proxy)

**Configuration:**

* **Memory**: Total RAM available (MB) - e.g., `32768` for 32GB
* **Memory Over-Allocation**: Percentage to over-allocate (e.g., `0` for none, `10` for 10% more)
* **Disk Space**: Total disk available (MB) - e.g., `500000` for \~488GB
* **Disk Over-Allocation**: Percentage to over-allocate

**Ports:**

* **Daemon Port**: `8080` (Wings communication)
* **Daemon SFTP Port**: `2022` (SFTP access for users)

**Advanced:**

* **Daemon Server File Directory**: `/var/lib/pterodactyl/volumes`
* **Maintenance Mode**: Disabled (enable to prevent new servers)

4. Click **Create Node**
5. **Copy the configuration** displayed after creation

#### Step 3: Install Wings

On the new node server:

**Install Docker:**

```bash
curl -fsSL https://get.docker.com | sh
systemctl enable --now docker
```

**Install Wings:**

```bash
mkdir -p /etc/pterodactyl
curl -L -o /usr/local/bin/wings "https://github.com/pterodactyl/wings/releases/latest/download/wings_linux_$([[ "$(uname -m)" == "x86_64" ]] && echo "amd64" || echo "arm64")"
chmod u+x /usr/local/bin/wings
```

**Create Systemd Service** (`/etc/systemd/system/wings.service`):

```ini
[Unit]
Description=Pterodactyl Wings Daemon
After=docker.service
Requires=docker.service
PartOf=docker.service

[Service]
User=root
WorkingDirectory=/etc/pterodactyl
LimitNOFILE=4096
PIDFile=/var/run/wings/daemon.pid
ExecStart=/usr/local/bin/wings
Restart=on-failure
StartLimitInterval=180
StartLimitBurst=30
RestartSec=5s

[Install]
WantedBy=multi-user.target
```

**Enable and Start:**

```bash
systemctl enable --now wings
```

#### Step 4: Configure Wings

1. **Create configuration file** (`/etc/pterodactyl/config.yml`):
   * Paste the configuration copied from Pterodactyl panel
   * Or download via API:

```bash
cd /etc/pterodactyl
curl -H "Authorization: Bearer YOUR_API_KEY" \
     "https://panel.example.com/api/application/nodes/NODE_ID/configuration" \
     | jq -r '.attributes.configuration' > config.yml
```

2. **Verify configuration:**

```bash
cat /etc/pterodactyl/config.yml
```

Should contain:

```yaml
debug: false
uuid: <node-uuid>
token_id: <token-id>
token: <token>
api:
  host: 0.0.0.0
  port: 8080
  ssl:
    enabled: true
    cert: /etc/letsencrypt/live/node.example.com/fullchain.pem
    key: /etc/letsencrypt/live/node.example.com/privkey.pem
system:
  data: /var/lib/pterodactyl/volumes
  sftp:
    bind_port: 2022
allowed_mounts: []
remote: https://panel.example.com
```

3. **Start Wings:**

```bash
systemctl restart wings
systemctl status wings
```

4. **Check Wings logs:**

```bash
journalctl -u wings -f
```

Look for successful connection to panel:

```
successfully configured wings
connected to panel successfully
```

#### Step 5: Verify Node Status

1. Return to **Pterodactyl Admin → Nodes**
2. Find your new node
3. Check status indicator (should be green/online)
4. Click node name to view details
5. Check **System Information** shows live stats (CPU, RAM, Disk)

### Allocations

After adding a node, configure allocations (IP addresses and ports) for servers:

1. **Navigate to node** in Pterodactyl Admin
2. Click **Allocation** tab
3. **Assign New Allocations:**
   * **IP Address**: Node's public IP (or specific IPs)
   * **IP Alias**: Optional display name
   * **Ports**: Range of ports (e.g., `25565-25665` for 100 ports)
4. Click **Submit**

**Allocation Best Practices:**

* Allocate ports in blocks (e.g., 25565-25665 for Minecraft)
* Use different port ranges for different game types
* Reserve some allocations for future use
* Document allocation schema for organization

### Node Resource Management

**Resource Limits:**

* **RAM**: Total physical RAM minus OS overhead (recommend 10-20% reserve)
* **Disk**: Total disk space minus OS/system usage
* **CPU**: Percentage-based allocation (100% = 1 core)

**Over-Allocation:**

* **Memory Over-Allocation**: Allocate more memory than physically available (risky)
* **Disk Over-Allocation**: Allocate more disk than physically available (risky)
* **Recommendation**: Start with 0% over-allocation, increase only if needed

**Example for 32GB RAM, 500GB Disk Node:**

* **Memory**: 28672 MB (28GB, leaving 4GB for OS)
* **Disk**: 450000 MB (\~439GB, leaving 50GB for OS/Docker)
* **Over-Allocation**: 0% initially

### Horizontal Scaling Strategy

**Small Deployment (50-200 servers):**

* 1-2 nodes
* Basic hardware (4 cores, 16GB RAM, 500GB SSD)

**Medium Deployment (200-1000 servers):**

* 3-5 nodes
* Mid-range hardware (8 cores, 32GB RAM, 1TB SSD)

**Large Deployment (1000+ servers):**

* 5-10+ nodes
* High-end hardware (16+ cores, 64GB+ RAM, 2TB+ SSD)
* Geographic distribution for latency
* Redundancy for high availability

***

## Monitoring and Health Checks

### PteroCA Monitoring

**Built-in Health Checks:**

* **Plugin Health**: Runs every 6 hours (checks integrity, dependencies, configuration)
* **Cron Tasks**: Scheduled tasks execute via `pteroca:cron:schedule`

**Application Monitoring:**

**Check Application Status:**

```bash
# Check if PHP-FPM is running
systemctl status php8.2-fpm

# Check if Nginx is running
systemctl status nginx

# Check application logs
tail -f /var/www/pteroca/var/log/prod.log
```

**Database Monitoring:**

```bash
# Check MySQL status
systemctl status mysql

# Check connections
mysql -e "SHOW STATUS LIKE 'Threads_connected';"

# Check slow queries
mysql -e "SHOW VARIABLES LIKE 'slow_query%';"
```

**Queue Monitoring:**

```bash
# Check messenger queue status
php bin/console messenger:stats

# View failed messages
php bin/console messenger:failed:show
```

### Pterodactyl Monitoring

**Node Status:**

1. Navigate to **Admin → Nodes**
2. Check status indicators (green = online, red = offline)
3. Click node to view detailed metrics

**Wings Monitoring:**

```bash
# Check Wings status
systemctl status wings

# View Wings logs
journalctl -u wings -f

# Check Wings API health
curl -k https://node.example.com:8080/api/system
```

**Server Metrics:**

* CPU usage per server
* Memory usage per server
* Disk usage per server
* Network I/O statistics

### External Monitoring Tools

**Recommended Tools:**

**Uptime Monitoring:**

* UptimeRobot
* Pingdom
* StatusCake

**Application Performance:**

* New Relic APM
* DataDog
* Sentry (error tracking)

**Infrastructure Monitoring:**

* Prometheus + Grafana
* Netdata
* Zabbix

**Log Aggregation:**

* ELK Stack (Elasticsearch, Logstash, Kibana)
* Graylog
* Loki + Grafana

***

## Backup Strategies

### PteroCA Backups

**Database Backups:**

```bash
#!/bin/bash
# Daily backup script
BACKUP_DIR="/backups/pteroca"
DATE=$(date +%Y%m%d_%H%M%S)

mysqldump -u root -p pteroca | gzip > "$BACKUP_DIR/pteroca_$DATE.sql.gz"

# Retain 30 days of backups
find $BACKUP_DIR -name "pteroca_*.sql.gz" -mtime +30 -delete
```

**Application Backups:**

```bash
#!/bin/bash
# Backup uploads and configuration
tar -czf /backups/pteroca/uploads_$DATE.tar.gz /var/www/pteroca/public/uploads
tar -czf /backups/pteroca/config_$DATE.tar.gz /var/www/pteroca/.env /var/www/pteroca/config
```

**Automated Backup with Cron:**

```cron
# Daily at 2 AM
0 2 * * * /usr/local/bin/backup-pteroca.sh
```

### Pterodactyl Backups

**Panel Database:**

```bash
mysqldump -u root -p pterodactyl | gzip > /backups/pterodactyl_$DATE.sql.gz
```

**Server Backups:** Pterodactyl includes built-in server backup functionality:

* Configure backup limits per server
* Backups stored on node or remote S3-compatible storage
* Users can create/restore backups via panel

***

## Security at Scale

### Network Security

**Firewall Rules (UFW Example):**

```bash
# PteroCA Server
ufw allow 80/tcp    # HTTP
ufw allow 443/tcp   # HTTPS
ufw allow 22/tcp    # SSH (from specific IPs only)

# Database Server (if separate)
ufw allow from <pteroca-ip> to any port 3306

# Redis Server (if separate)
ufw allow from <pteroca-ip> to any port 6379

# Pterodactyl Nodes
ufw allow 8080/tcp  # Wings API
ufw allow 2022/tcp  # SFTP
ufw allow 25565:25665/tcp  # Game server ports
```

**Internal Network:**

* Use private network for backend communication (database, Redis, etc.)
* Restrict public access to application and load balancer only
* Implement VPN for administrative access

### Application Security

**SSL/TLS:**

* Use Let's Encrypt for free SSL certificates
* Configure HSTS (HTTP Strict Transport Security)
* Disable weak ciphers and protocols

**Nginx SSL Configuration:**

```nginx
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
```

**Rate Limiting:**

```nginx
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;

location /login {
    limit_req zone=login burst=10;
}
```

### Database Security

**Secure MySQL:**

```sql
-- Remove anonymous users
DELETE FROM mysql.user WHERE User='';

-- Remove test database
DROP DATABASE IF EXISTS test;

-- Create dedicated user for PteroCA
CREATE USER 'pteroca'@'localhost' IDENTIFIED BY 'strong_password';
GRANT ALL PRIVILEGES ON pteroca.* TO 'pteroca'@'localhost';
FLUSH PRIVILEGES;
```

**Backup Encryption:**

```bash
mysqldump -u root -p pteroca | gzip | openssl enc -aes-256-cbc -salt -out pteroca_$DATE.sql.gz.enc
```

***

## API Rate Limits

In cases of intensive use of the Pterodactyl API, adjust request limits to avoid errors:

**Pterodactyl `.env` Configuration:**

```env
# Client API rate limit (requests per minute)
APP_API_CLIENT_RATELIMIT=720

# Application API rate limit (requests per minute)
APP_API_APPLICATION_RATELIMIT=240
```

**Recommendations:**

* **Small Deployment**: Default limits (240/720) sufficient
* **Medium Deployment**: Increase to 480/1440
* **Large Deployment**: Increase to 1200/2880 or higher

**Monitor API Usage:**

```bash
# Check Pterodactyl logs for rate limit errors
grep "rate limit" /var/www/pterodactyl/storage/logs/laravel-*.log
```

***

## Troubleshooting at Scale

### High Load Issues

**Symptoms:**

* Slow page load times
* Timeouts
* 502/504 gateway errors

**Diagnosis:**

```bash
# Check CPU and memory usage
top
htop

# Check PHP-FPM status
systemctl status php8.2-fpm

# Check error logs
tail -f /var/log/nginx/error.log
tail -f /var/www/pteroca/var/log/prod.log
```

**Solutions:**

* Increase PHP-FPM `pm.max_children`
* Add more web instances (horizontal scaling)
* Optimize database queries
* Implement caching (Redis)
* Enable OPcache if not already enabled

### Session Issues

**Symptoms:**

* Users logged out randomly
* Session data lost
* Login loops

**Causes:**

* File-based sessions with multiple instances
* Session directory permissions
* Session garbage collection issues

**Solutions:**

* Migrate to Redis session storage
* Ensure session directory is writable
* Configure session affinity on load balancer

### Database Connection Issues

**Symptoms:**

* "Too many connections" errors
* Slow query execution
* Connection timeouts

**Diagnosis:**

```sql
SHOW STATUS LIKE 'Threads_connected';
SHOW STATUS LIKE 'Max_used_connections';
SHOW VARIABLES LIKE 'max_connections';
```

**Solutions:**

* Increase `max_connections` in MySQL
* Implement connection pooling (ProxySQL)
* Optimize long-running queries
* Add read replicas for read-heavy operations

### Queue Processing Issues

**Symptoms:**

* Emails not sending
* Background tasks not completing
* Queue messages pile up

**Diagnosis:**

```bash
# Check messenger status
php bin/console messenger:stats

# View failed messages
php bin/console messenger:failed:show
```

**Solutions:**

* Increase worker count
* Switch to Redis-backed queue
* Check external service availability (SMTP, etc.)
* Retry failed messages: `php bin/console messenger:failed:retry`

***

## Best Practices

### Capacity Planning

**Monitor Growth:**

* Track user registrations per month
* Track server creations per month
* Monitor resource usage trends
* Plan capacity 3-6 months ahead

**Scaling Triggers:**

* CPU usage consistently >70%
* RAM usage consistently >80%
* Disk space <20% remaining
* Response times >1 second average

### Deployment Strategy

**Blue-Green Deployment:**

1. Deploy new version to "blue" environment
2. Test thoroughly
3. Switch load balancer to blue
4. Keep green as rollback option

**Rolling Updates:**

1. Update one instance at a time
2. Verify health before next instance
3. Minimal downtime

**Canary Releases:**

1. Deploy to subset of instances
2. Monitor for issues
3. Gradually roll out to all instances

### Documentation

**Maintain Runbooks:**

* Deployment procedures
* Rollback procedures
* Incident response plans
* Configuration management

**Infrastructure as Code:**

* Use Ansible, Terraform, or similar
* Version control infrastructure configs
* Automate provisioning and scaling

### Testing

**Load Testing:**

```bash
# Apache Bench example
ab -n 10000 -c 100 https://pteroca.example.com/

# Siege example
siege -c 100 -t 5M https://pteroca.example.com/
```

**Performance Testing:**

* Test database queries under load
* Test API response times
* Test queue processing speed
* Test backup and restore procedures

***

## Conclusion

Scaling PteroCA and Pterodactyl infrastructure requires careful planning and implementation. Start with a single-instance deployment and scale horizontally as your business grows. Monitor performance metrics, implement caching and queue systems, and ensure high availability through redundancy and load balancing.

**Key Takeaways:**

* Single instance sufficient for up to 500 customers
* Redis essential for horizontal scaling
* Shared storage required for multiple instances
* Pterodactyl scales by adding nodes, not panel instances
* Monitor, backup, and document everything

***

## Related Documentation

**PteroCA Configuration:**

* [System Configuration](/core-configuration/core-configuration.md) - Initial setup and configuration
* [Database Configuration](/core-configuration/core-configuration/general-settings.md) - Database setup details

**Pterodactyl Integration:**

* [PteroCA Pterodactyl Addon](/core-configuration/pterodactyl-integration/addon-installation.md) - Required addon setup
* [API Configuration](/core-configuration/pterodactyl-integration/api-configuration.md) - API integration guide

**Advanced Topics:**

* [Docker Deployment](https://github.com/PteroCA-Org/panel) - Container-based deployment
* [Security Best Practices](https://github.com/PteroCA-Org/pteroca-homepage/blob/docs/help-and-maintenance/troubleshooting/README.md) - Security hardening guide

**External Resources:**

* [Official Pterodactyl Documentation](https://pterodactyl.io/)
* [Wings Documentation](https://pterodactyl.io/wings/1.0/)
* [Symfony Performance](https://symfony.com/doc/current/performance.html)
* [MySQL Performance Tuning](https://dev.mysql.com/doc/refman/8.0/en/optimization.html)

{% hint style="success" %}
**Need Help?**

If you need further assistance or have questions about scaling your infrastructure with PteroCA and Pterodactyl, join our [Discord community](https://discord.com/invite/Gz5phhuZym) or contact technical support.
{% endhint %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.pteroca.com/advanced-topics/scaling-infrastructure.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
