SFTPGo on a VPS: Self-Hosted SFTP, WebDAV, and S3 File Server with User Management

SFTPGo on a VPS: Self-Hosted SFTP, WebDAV, and S3 File Server with User Management

SFTPGo is an open-source, full-featured file transfer server that supports SFTP, FTP/FTPS, WebDAV, and an S3-compatible HTTP API — all in one binary. It provides per-user home directories, storage quotas, bandwidth limits, virtual folders mapping external storage (S3, GCS, Azure Blob), event-driven webhooks, and an admin web UI. For businesses needing to provide secure file transfer access to clients or internal teams, SFTPGo replaces expensive managed SFTP services.

What SFTPGo Provides

  • Protocols: SFTP, SCP, FTP/FTPS/FTPES, WebDAV (HTTPS), HTTP/S3-compatible API
  • User management: Per-user home directories, quotas (space + file count), bandwidth limits
  • Virtual folders: Map S3 buckets, GCS buckets, or local paths as user folders
  • Auth providers: Password, SSH key, TLS certificate, OpenID Connect
  • Events: Webhooks on upload/download/delete for automation
  • Roles: Admin, regular user, sub-admin

Step 1: Docker Compose Setup

<code">mkdir -p /opt/sftpgo && cd /opt/sftpgo
nano docker-compose.yml
<code">version: '3.8'

services:
  sftpgo:
    image: drakkan/sftpgo:v2-alpine
    container_name: sftpgo
    restart: always
    ports:
      - "2022:2022"    # SFTP (use 22 if your SSH is on another port)
      - "127.0.0.1:8080:8080"   # WebUI and REST API
      - "127.0.0.1:10080:10080" # WebDAV (proxied via Nginx)
    environment:
      SFTPGO_HTTPD__BINDINGS__0__PORT: 8080
      SFTPGO_HTTPD__BINDINGS__0__ADDRESS: ""
      SFTPGO_WEBDAVD__BINDINGS__0__PORT: 10080
      SFTPGO_SFTPD__BINDINGS__0__PORT: 2022
      SFTPGO_DATA_PROVIDER__DRIVER: sqlite
      SFTPGO_DATA_PROVIDER__NAME: /var/lib/sftpgo/sftpgo.db
    volumes:
      - sftpgo_data:/var/lib/sftpgo
      - ./user_home:/srv/sftpgo/data   # User home directories

volumes:
  sftpgo_data:
<code">mkdir -p user_home
docker compose up -d
docker compose logs -f sftpgo

Step 2: Nginx Reverse Proxy

<code">sudo nano /etc/nginx/sites-available/sftpgo
<code"># Web Admin UI and REST API
server {
    listen 443 ssl http2;
    server_name files.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/files.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/files.yourdomain.com/privkey.pem;

    client_max_body_size 10000M;
    proxy_read_timeout 600s;

    location / {
        proxy_pass http://127.0.0.1:8080;
        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;
        proxy_request_buffering off;
    }
}

# WebDAV endpoint
server {
    listen 443 ssl http2;
    server_name webdav.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/files.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/files.yourdomain.com/privkey.pem;

    client_max_body_size 10000M;
    proxy_read_timeout 600s;

    location / {
        proxy_pass http://127.0.0.1:10080;
        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;
        proxy_request_buffering off;
    }
}
<code">sudo certbot --nginx -d files.yourdomain.com -d webdav.yourdomain.com
sudo systemctl reload nginx

Step 3: Initial Admin Setup

<code"># Create admin user:
docker exec -it sftpgo sftpgo initprovider && \
docker exec -it sftpgo sftpgo resetpwd --admin

# Or via first-run setup:
# Visit https://files.yourdomain.com/web/admin/
# On first visit: create admin credentials

Step 4: Create Users via REST API

<code"># Create a user with quota and SFTP access:
curl -X POST https://files.yourdomain.com/api/v2/users \
  -H "Authorization: Basic $(echo -n 'admin:password' | base64)" \
  -H "Content-Type: application/json" \
  -d '{
    "username": "client-acme",
    "password": "SecureClientPassword!",
    "home_dir": "/srv/sftpgo/data/acme",
    "uid": 1000,
    "gid": 1000,
    "max_sessions": 2,
    "quota_size": 10737418240,  # 10 GB in bytes
    "quota_files": 10000,
    "upload_bandwidth": 5120,   # 5 MB/s upload limit
    "download_bandwidth": 10240, # 10 MB/s download limit
    "permissions": {
      "/": ["list", "download", "upload", "delete", "rename", "create_dirs"]
    },
    "filters": {
      "allowed_extensions": [".pdf", ".docx", ".xlsx", ".zip", ".csv"],
      "denied_protocols": ["FTP"]   # Force SFTP only
    }
  }'

Step 5: SSH Key Authentication for Users

<code"># Add SSH public key to user (more secure than password):
curl -X PUT https://files.yourdomain.com/api/v2/users/client-acme \
  -H "Authorization: Basic $(echo -n 'admin:password' | base64)" \
  -H "Content-Type: application/json" \
  -d '{
    "public_keys": [
      "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... client@company.com"
    ]
  }'

# Client connects with:
sftp -i ~/.ssh/id_ed25519 -P 2022 client-acme@files.yourdomain.com

Step 6: Virtual Folders (S3 Buckets as User Directories)

<code"># Map an S3 bucket as a virtual folder for a user:
# Admin UI → Folders → Add Folder
# Name: acme-s3-folder
# Storage: S3
# Bucket: acme-client-bucket
# Region: us-east-1
# Access Key / Secret: your S3 credentials

# Attach to user as virtual folder:
# User → Edit → Virtual Folders → add acme-s3-folder at path /s3

# User now sees /s3/ directory which maps to their S3 bucket
# No VPS disk used — direct S3 upload/download via SFTP

Step 7: Event Webhooks

<code"># Trigger webhooks on file events:
# Admin UI → Event Manager → Add Rule

# Example: notify when client uploads a file
# Trigger: Upload completed
# Target: Webhook → https://your-app.com/api/file-uploaded
# Payload:
{
  "action": "upload",
  "username": "{{.Name}}",
  "fs_path": "{{.VirtualPath}}",
  "file_size": "{{.FileSize}}",
  "timestamp": "{{.Timestamp}}"
}

# Or: virus scan on upload
# Trigger: Pre-upload check
# Target: External command → /usr/local/bin/clamav-check.sh

Getting Started

SFTPGo uses 50–150 MB RAM — it runs on a 1 GB Ubuntu VPS at VPS.DO. For clients uploading large files, NVMe storage provides substantially better throughput than HDD-backed VPS instances. Exposing SFTP on port 2022 (not 22) avoids conflicts with system SSH and simplifies firewall rules. The REST API makes user provisioning fully automatable from your application’s onboarding flow.

Conclusion

SFTPGo consolidates SFTP, FTP, WebDAV, and HTTP file transfer into one service with per-user quotas, bandwidth limits, SSH key auth, and event webhooks. For businesses providing secure file exchange to clients (accountants sharing tax documents, agencies receiving design assets, healthcare providers exchanging records), self-hosted SFTPGo replaces expensive managed SFTP services at VPS cost. Virtual folder mapping to S3 enables unlimited storage without local disk constraints.

1 Comment

Post Your Comment

Fast • Reliable • Affordable VPS - DO It Now!

Get top VPS hosting with VPS.DO’s fast, low-cost plans. Try risk-free with our 7-day no-questions-asked refund and start today!