# Deploying Potato on a Server

Source: https://www.potatoannotator.com/blog/server-deployment-guide

This guide covers running Potato in production: server setup, Docker, the main cloud platforms, and the security configuration you'll want before exposing anything to the internet. The [deployment documentation](https://github.com/davidjurgens/potato/blob/master/docs/deployment/usage.md) on GitHub goes deeper on a few of these topics.

## Deployment Options

| Method | Best For | Complexity |
|--------|----------|------------|
| Local | Development, testing | Low |
| Docker | Reproducible deployments | Medium |
| Cloud VM | Full control, custom setup | Medium |
| Docker Compose | Multi-container setups | Medium |
| Kubernetes | Large-scale deployments | High |

## Basic Server Deployment

### Prerequisites

```bash
# Ubuntu/Debian server
sudo apt update
sudo apt install python3.9 python3-pip nginx certbot python3-certbot-nginx

# Create directory structure
mkdir -p /opt/potato/projects
cd /opt/potato
```

### Installation

```bash
# Create virtual environment
python3 -m venv venv
source venv/bin/activate

# Install Potato
pip install potato-annotation

# Verify installation
potato --version
```

### Running with Gunicorn

```bash
# Install production server
pip install gunicorn

# Run with gunicorn
gunicorn -w 4 -b 127.0.0.1:8000 \
  --chdir /opt/potato/projects/my_project \
  "potato.server:create_app(config_path='config.yaml')"
```

### Systemd Service

Create `/etc/systemd/system/potato.service`:

```ini
[Unit]
Description=Potato Annotation Server
After=network.target

[Service]
Type=simple
User=potato
Group=potato
WorkingDirectory=/opt/potato/projects/my_project
Environment="PATH=/opt/potato/venv/bin"
ExecStart=/opt/potato/venv/bin/gunicorn -w 4 -b 127.0.0.1:8000 \
  "potato.server:create_app(config_path='config.yaml')"
Restart=always
RestartSec=10

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

Enable and start:

```bash
sudo systemctl enable potato
sudo systemctl start potato
sudo systemctl status potato
```

## Nginx Configuration

### Basic Reverse Proxy

Create `/etc/nginx/sites-available/potato`:

```nginx
server {
    listen 80;
    server_name annotation.example.com;

    location / {
        proxy_pass http://127.0.0.1:8000;
        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;

        # WebSocket support (if needed)
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }

    # Static files
    location /static/ {
        alias /opt/potato/projects/my_project/static/;
        expires 30d;
        add_header Cache-Control "public, no-transform";
    }

    # Upload size limit
    client_max_body_size 100M;
}
```

Enable the site:

```bash
sudo ln -s /etc/nginx/sites-available/potato /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
```

### SSL with Let's Encrypt

```bash
# Get SSL certificate
sudo certbot --nginx -d annotation.example.com

# Auto-renewal
sudo systemctl enable certbot.timer
```

## Docker Deployment

### Dockerfile

```dockerfile
FROM python:3.9-slim

WORKDIR /app

# Install dependencies
RUN pip install potato-annotation gunicorn

# Copy project files
COPY config.yaml .
COPY data/ ./data/

# Create directories
RUN mkdir -p annotations logs

# Expose port
EXPOSE 8000

# Run server
CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:8000", \
     "potato.server:create_app(config_path='config.yaml')"]
```

### Build and Run

```bash
# Build image
docker build -t potato-annotation .

# Run container
docker run -d \
  --name potato \
  -p 8000:8000 \
  -v $(pwd)/annotations:/app/annotations \
  -v $(pwd)/logs:/app/logs \
  potato-annotation
```

### Docker Compose

```yaml
# docker-compose.yml
version: '3.8'

services:
  potato:
    build: .
    ports:
      - "8000:8000"
    volumes:
      - ./data:/app/data:ro
      - ./annotations:/app/annotations
      - ./logs:/app/logs
    environment:
      - POTATO_ENV=production
    restart: unless-stopped

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
      - ./ssl:/etc/nginx/ssl:ro
    depends_on:
      - potato
    restart: unless-stopped
```

## Cloud Platform Deployment

### AWS EC2

```bash
# Launch EC2 instance (Ubuntu 22.04)
# Configure security group: ports 22, 80, 443

# SSH to instance
ssh -i your-key.pem ubuntu@your-instance-ip

# Follow basic server deployment steps above

# For persistent storage, attach EBS volume
sudo mount /dev/xvdf /opt/potato/data
```

### Google Cloud Platform

```bash
# Create VM instance
gcloud compute instances create potato-server \
  --zone=us-central1-a \
  --machine-type=e2-medium \
  --image-family=ubuntu-2204-lts \
  --image-project=ubuntu-os-cloud

# Configure firewall
gcloud compute firewall-rules create allow-http \
  --allow tcp:80,tcp:443
```

### DigitalOcean

```bash
# Create droplet via CLI
doctl compute droplet create potato-server \
  --size s-2vcpu-4gb \
  --image ubuntu-22-04-x64 \
  --region nyc1
```

## Configuration for Production

### Production Config

```yaml
# config.yaml
annotation_task_name: "Production Annotation Task"

# Note: Server settings (host, port, workers) are CLI flags:
#   potato -p 8000 --host 0.0.0.0 config.yaml
# Or use gunicorn for production with multiple workers

# Logging
logging:
  level: INFO
  file: logs/potato.log
  format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
  rotation:
    max_size: 10MB
    backup_count: 5

# Data
data_files:
  - items.jsonl

item_properties:
  id_key: id
  text_key: text

# Output
output_annotation_dir: annotations/
export_annotation_format: jsonl
```

### Environment Variables

```bash
# /opt/potato/.env
POTATO_SECRET_KEY=your-secure-random-key-here
POTATO_ENV=production
OPENAI_API_KEY=sk-...  # If using AI features
```

Load in systemd:

```ini
[Service]
EnvironmentFile=/opt/potato/.env
```

## Security best practices

If you are open-sourcing the project or collecting data from the public, also read the [open-sourcing guide](https://github.com/davidjurgens/potato/blob/master/docs/deployment/open-sourcing.md) for the privacy and licensing pieces this section doesn't cover.

### Authentication

```yaml
user_config:
  auth_type: password

  # Strong password requirements
  password_policy:
    min_length: 12
    require_uppercase: true
    require_number: true

  # Session security
  session:
    secure_cookie: true
    http_only: true
    same_site: strict
```

### HTTPS Configuration

```nginx
server {
    listen 443 ssl http2;
    server_name annotation.example.com;

    ssl_certificate /etc/letsencrypt/live/annotation.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/annotation.example.com/privkey.pem;

    # Modern SSL configuration
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
    ssl_prefer_server_ciphers off;

    # Security headers
    add_header Strict-Transport-Security "max-age=63072000" always;
    add_header X-Frame-Options DENY;
    add_header X-Content-Type-Options nosniff;
    add_header X-XSS-Protection "1; mode=block";

    # ... rest of config
}
```

### Firewall

```bash
# UFW configuration
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw allow 'Nginx Full'
sudo ufw enable
```

## Monitoring

### Health Check Endpoint

```bash
# Check server health
curl http://localhost:8000/health

# Expected response
{"status": "healthy", "version": "2.0.0"}
```

### Log Monitoring

```bash
# View logs
sudo journalctl -u potato -f

# Or check log file
tail -f /opt/potato/logs/potato.log
```

### Prometheus Metrics (Optional)

```yaml
# config.yaml
monitoring:
  prometheus:
    enabled: true
    port: 9090
```

## Backup Strategy

### Automated Backups

```bash
# backup.sh
#!/bin/bash
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR=/opt/potato/backups

# Backup annotations
tar -czf $BACKUP_DIR/annotations_$DATE.tar.gz /opt/potato/projects/*/annotations/

# Backup configs
tar -czf $BACKUP_DIR/configs_$DATE.tar.gz /opt/potato/projects/*/config.yaml

# Remove old backups (keep 7 days)
find $BACKUP_DIR -mtime +7 -delete

# Optional: sync to cloud storage
# aws s3 sync $BACKUP_DIR s3://your-bucket/potato-backups/
```

Add to crontab:

```bash
# Run daily at 2am
0 2 * * * /opt/potato/backup.sh
```

## Scaling

### Multiple Workers

```bash
# Increase gunicorn workers based on CPU cores
gunicorn -w $((2 * $(nproc) + 1)) -b 0.0.0.0:8000 ...
```

### Load Balancing

```nginx
upstream potato_servers {
    server 127.0.0.1:8001;
    server 127.0.0.1:8002;
    server 127.0.0.1:8003;
}

server {
    location / {
        proxy_pass http://potato_servers;
    }
}
```

## Troubleshooting

### Common Issues

**Port already in use:**
```bash
sudo lsof -i :8000
sudo kill -9 <PID>
```

**Permission denied:**
```bash
sudo chown -R potato:potato /opt/potato
```

**Out of memory:**
```bash
# Add swap space
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
```

---

*For additional help, see the [full documentation](/docs/getting-started/installation) or [GitHub discussions](https://github.com/davidjurgens/potato/discussions).*
