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)