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 g
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
2. Create the environment file:
$ nano .env
DOMAIN=sftp.example.com
LETSENCRYPT_EMAIL=[email protected]
Deploy with Docker Compose
1. Create the Compose manifest:
$ nano docker-compose.yml
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
2. Start the services and view logs:
$ docker compose up -d
$ docker compose ps
$ docker compose logs
Initial Admin Setup
- Open
https://sftp.example.com/web/admin. - Create the administrator account.
- 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
Redeploy:
$ docker compose up -d
Trusted IPs go under IP Manager β IP Lists; auto-banned IPs appear under Auto Block List.
Create a User (Password + SSH Key)
-
Users β Add: username, password, status Active, file system Local disk, root directory
/srv/sftpgo/USERNAME. - (Optional) Generate an SSH key on a client:
$ ssh-keygen -t ed25519 -f ~/.ssh/sftpgo_key -C "sftp-user"
$ cat ~/.ssh/sftpgo_key.pub
- Users β Edit β Public keys β paste the public key.
- Test:
$ sftp -i ~/.ssh/sftpgo_key -P 2022 [email protected]
- (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:
5MB Β· Upload Concurrency:2
Save and round-trip a test file:
$ echo "Hello SFTPGo" > testfile.txt
$ sftp -P 2022 [email protected]
sftp> put testfile.txt
sftp> ls
sftp> exit
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"
2. Create the SFTPGo JSON config:
$ nano ~/sftpgo/config/sftpgo.json
{
"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}
]
}
}
3. Expose the passive port range in docker-compose.yml:
- "50000-50100:50000-50100"
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/
Add Quotas, Bandwidth Limits, and IP ACLs
Per-user Edit β Disk quota and bandwidth limits:
-
Quota size:
10GB(0= unlimited) Β· Quota files:10000 -
Bandwidth UL/DL (KB/s): e.g.
5120for 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
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
$ docker compose up -d
2. Set up webhook notifications:
-
Event Manager β Actions β Add: HTTP, set the server URL and
POSTmethod. - 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.
Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes β full credit and traffic to the original publisher.