DEV Community

Cover image for Deploying SFTPGo as an AWS Transfer Family Alternative on Ubuntu
Sanskriti Harmukh for Vultr

Posted on with Aashish Chaurasiya Originally published at docs.vultr.com

Deploying SFTPGo as an AWS Transfer Family Alternative on Ubuntu

SFTPGo is an open-source file transfer server that exposes SFTP, FTP/S, and WebDAV with pluggable storage backends and a web admin UI, a self-hosted alternative to AWS Transfer Family without per-endpoint billing. This guide deploys SFTPGo using Docker Compose with Traefik handling automatic HTTPS for the admin UI, SQLite for the data provider, and protocol ports for SFTP, FTP/S, and WebDAV exposed on the host. By the end, you'll have SFTPGo accepting multi-protocol file transfers backed by either local disk or S3-compatible object storage.


Set Up the Directory Structure

1. Create the project directories:

$ mkdir -p ~/sftpgo/{data,config}
$ cd ~/sftpgo
Enter fullscreen mode Exit fullscreen mode

2. Create the environment file:

$ nano .env
Enter fullscreen mode Exit fullscreen mode
DOMAIN=sftp.example.com
LETSENCRYPT_EMAIL=admin@example.com
Enter fullscreen mode Exit fullscreen mode

Deploy with Docker Compose

1. Create the Compose manifest:

$ nano docker-compose.yml
Enter fullscreen mode Exit fullscreen mode
services:
  traefik:
    image: traefik:v3.6
    container_name: traefik
    command:
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--entrypoints.web.http.redirections.entrypoint.to=websecure"
      - "--certificatesresolvers.letsencrypt.acme.httpchallenge=true"
      - "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
      - "--certificatesresolvers.letsencrypt.acme.email=${LETSENCRYPT_EMAIL}"
      - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - "./letsencrypt:/letsencrypt"
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
    restart: unless-stopped

  sftpgo:
    image: drakkan/sftpgo:v2.6
    container_name: sftpgo
    restart: unless-stopped
    ports:
      - "2022:2022"   # SFTP
      - "2121:2121"   # FTP/S control
      - "10080:10080" # WebDAV
    environment:
      - SFTPGO_HTTPD__BINDINGS__0__PORT=8080
      - SFTPGO_HTTPD__BINDINGS__0__ADDRESS=0.0.0.0
      - SFTPGO_SFTPD__BINDINGS__0__PORT=2022
      - SFTPGO_FTPD__BINDINGS__0__PORT=2121
      - SFTPGO_WEBDAVD__BINDINGS__0__PORT=10080
      - SFTPGO_DATA_PROVIDER__DRIVER=sqlite
      - SFTPGO_DATA_PROVIDER__NAME=/var/lib/sftpgo/sftpgo.db
    volumes:
      - ./data:/srv/sftpgo
      - ./config:/var/lib/sftpgo
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.sftpgo.rule=Host(`${DOMAIN}`)"
      - "traefik.http.routers.sftpgo.entrypoints=websecure"
      - "traefik.http.routers.sftpgo.tls.certresolver=letsencrypt"
      - "traefik.http.services.sftpgo.loadbalancer.server.port=8080"
    healthcheck:
      test: ["CMD", "sftpgo", "ping", "-c", "/var/lib/sftpgo"]
      interval: 30s
      timeout: 10s
      retries: 3
Enter fullscreen mode Exit fullscreen mode

2. Start the services and view logs:

$ docker compose up -d
$ docker compose ps
$ docker compose logs
Enter fullscreen mode Exit fullscreen mode

Initial Admin Setup

  1. Open https://sftp.example.com/web/admin.
  2. Create the administrator account.
  3. Enable two-factor authentication, scan the QR code with an authenticator app, and save the recovery codes.

Enable the Defender (Brute-Force Protection)

Edit docker-compose.yml and add to the sftpgo service environment: block:

- SFTPGO_COMMON__DEFENDER__ENABLED=true
- SFTPGO_COMMON__DEFENDER__DRIVER=memory
- SFTPGO_COMMON__DEFENDER__BAN_TIME=30
- SFTPGO_COMMON__DEFENDER__THRESHOLD=15
- SFTPGO_COMMON__DEFENDER__SCORE_INVALID=2
- SFTPGO_COMMON__DEFENDER__SCORE_VALID=1
Enter fullscreen mode Exit fullscreen mode

Redeploy:

$ docker compose up -d
Enter fullscreen mode Exit fullscreen mode

Trusted IPs go under IP Manager → IP Lists; auto-banned IPs appear under Auto Block List.


Create a User (Password + SSH Key)

  1. Users → Add: username, password, status Active, file system Local disk, root directory /srv/sftpgo/USERNAME.
  2. (Optional) Generate an SSH key on a client:
$ ssh-keygen -t ed25519 -f ~/.ssh/sftpgo_key -C "sftp-user"
$ cat ~/.ssh/sftpgo_key.pub
Enter fullscreen mode Exit fullscreen mode
  1. Users → Edit → Public keys — paste the public key.
  2. Test:
$ sftp -i ~/.ssh/sftpgo_key -P 2022 USERNAME@sftp.example.com
Enter fullscreen mode Exit fullscreen mode
  1. (Optional) Require 2FA per-protocol under Edit → ACLs → Require 2FA for.

Connect S3-Compatible Object Storage

In the user's Edit → File system block:

  • File system: S3 (Compatible)
  • Bucket: BUCKET_NAME
  • Region: REGION_CODE
  • Access Key / Access Secret: S3 credentials
  • Endpoint: https://YOUR_S3_ENDPOINT
  • Use path-style addressing: enabled
  • Upload Part Size: 5 MB · Upload Concurrency: 2

Save and round-trip a test file:

$ echo "Hello SFTPGo" > testfile.txt
$ sftp -P 2022 USERNAME@sftp.example.com
sftp> put testfile.txt
sftp> ls
sftp> exit
Enter fullscreen mode Exit fullscreen mode

S3 caveats: resume uploads disabled, no symlinks, non-atomic directory rename, chmod/chown ignored.


Enable FTP/S and WebDAV

1. Generate a self-signed cert for FTP/S:

$ openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
    -keyout ~/sftpgo/config/server.key \
    -out ~/sftpgo/config/server.crt \
    -subj "/CN=sftp.example.com"
Enter fullscreen mode Exit fullscreen mode

2. Create the SFTPGo JSON config:

$ nano ~/sftpgo/config/sftpgo.json
Enter fullscreen mode Exit fullscreen mode
{
  "ftpd": {
    "bindings": [
      {
        "port": 2121,
        "address": "",
        "tls_mode": 1,
        "certificate_file": "/var/lib/sftpgo/server.crt",
        "certificate_key_file": "/var/lib/sftpgo/server.key"
      }
    ],
    "passive_port_range": {"start": 50000, "end": 50100}
  },
  "webdavd": {
    "bindings": [
      {"port": 10080, "address": "", "enable_https": false}
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

3. Expose the passive port range in docker-compose.yml:

- "50000-50100:50000-50100"
Enter fullscreen mode Exit fullscreen mode

4. Restart and test:

$ docker compose restart sftpgo
$ lftp -u USERNAME -p 2121 ftp://sftp.example.com
$ curl -u USERNAME:PASSWORD http://sftp.example.com:10080/
Enter fullscreen mode Exit fullscreen mode

Add Quotas, Bandwidth Limits, and IP ACLs

Per-user EditDisk quota and bandwidth limits:

  • Quota size: 10GB (0 = unlimited) · Quota files: 10000
  • Bandwidth UL/DL (KB/s): e.g. 5120 for 5 MB/s

Per-user ACLs:

  • Allowed IP/Mask: 192.168.1.0/24, 10.0.0.0/8
  • Denied IP/Mask: specific blocked addresses
  • Per-directory permissions: restrict each path's operations

File Logging and Webhooks

1. Enable file logging:

$ mkdir -p ~/sftpgo/logs
Enter fullscreen mode Exit fullscreen mode

Add to the sftpgo service:

volumes:
  - ./logs:/var/log/sftpgo
environment:
  - SFTPGO_LOG_FILE_PATH=/var/log/sftpgo/sftpgo.log
  - SFTPGO_LOG_MAX_SIZE=10
  - SFTPGO_LOG_MAX_BACKUPS=5
  - SFTPGO_LOG_MAX_AGE=28
  - SFTPGO_LOG_LEVEL=info
Enter fullscreen mode Exit fullscreen mode
$ docker compose up -d
Enter fullscreen mode Exit fullscreen mode

2. Set up webhook notifications:

  • Event Manager → Actions → Add: HTTP, set the server URL and POST method.
  • Event Manager → Rules → Add: trigger on filesystem events (e.g. Upload) and attach the webhook action.

Next Steps

SFTPGo is running with multi-protocol access and HTTPS for the admin UI. From here you can:

  • Plug an external Postgres or MySQL backend by switching the data provider env vars
  • Generate user API tokens and provision accounts via the REST API for migrations
  • Front the SFTP port with a VPN (WireGuard) for tighter network isolation

For the full guide with additional tips, visit the original article on Vultr Docs.

Top comments (0)