sftproo ships as a single container image that runs the SFTP server, the web admin UI, and an optional backup service. This section walks through a self-hosted deployment with Docker Compose and the two cloud marketplace images.
2222 (SFTP) and 8443 (web UI) reachable from your users1. Create a directory for sftproo and move into it:
mkdir /opt/sftproo
cd /opt/sftproo
2. Create a docker-compose.yml file. The snippet below is a minimal, security-sane starting point — each environment variable is explained in the Environment Variables section. Replace every change_me placeholder with a long random value before you start the stack:
services:
sftproo:
image: public.ecr.aws/solvedevops/sftproo:latest
container_name: sftproo
ports:
- "2222:2222" # SFTP
- "8443:8443" # Web UI (HTTPS)
volumes:
- ./data:/data
- ./config:/etc/sftproo
environment:
- SFTPROO_HOST=0.0.0.0
- SFTPROO_PORT=2222
- SFTPROO_WEB_PORT=8443
- SFTPROO_DB_HOST=mariadb
- SFTPROO_DB_PORT=3306
- SFTPROO_DB_NAME=sftproo
- SFTPROO_DB_USER=sftproo
- SFTPROO_DB_PASSWORD=change_me_db_password
- SFTPROO_STORAGE_TYPE=local
- SFTPROO_STORAGE_PATH=/data
- SSH_HOST_KEY_PATH=/etc/sftproo/ssh_host_key
- SFTPROO_CERT_FILE=/etc/sftproo/sftproo-server.crt
- SFTPROO_KEY_FILE=/etc/sftproo/sftproo-server.pem
- SFTPROO_SESSION_SECRET=change_me_long_random_hex
- SFTPROO_ENCRYPTION_KEY=change_me_long_random_passphrase
# Leave SFTPROO_DEFAULT_ADMIN_PASSWORD unset to have a random one-time
# password printed to the container logs on first start.
depends_on:
- mariadb
restart: unless-stopped
mariadb:
image: mariadb:10.11
container_name: sftproo-db
environment:
- MYSQL_ROOT_PASSWORD=change_me_mariadb_root
- MYSQL_DATABASE=sftproo
- MYSQL_USER=sftproo
- MYSQL_PASSWORD=change_me_db_password
volumes:
- ./database:/var/lib/mysql
restart: unless-stopped
3. Start the services:
docker-compose up -d
4. Follow the logs to grab the one-time admin password, then open the web UI:
docker-compose logs -f sftproo
# Look for a line that begins with:
# Password: ... (shown only once)
# Then visit:
https://your-server:8443
SFTPROO_DEFAULT_ADMIN_PASSWORD explicitly in the compose file the forced-change flow is skipped; the random-password path is safer unless you need a known value for automation.
sftproo is available as a pre-configured AMI in the AWS Marketplace:
t3.small or larger is a reasonable default; size up if you expect heavy concurrent transfers.22 (SSH), 2222 (SFTP) and 8443 (web UI) from the source ranges you trust.ssh -i YOUR_SSH_KEY ubuntu@instance.ip.address
/opt/default-sftproo-credentials.txt for the admin username and unique initial password:(username: admin, password: unique-password)
https://ip.add.re.ss:8443 and sign in with those credentials. You will be prompted to set a new password before anything else.Deploy sftproo from the Azure Marketplace:
B2s or larger is a reasonable default.22, 2222 and 8443.ssh -i YOUR_SSH_KEY azureuser@instance.ip.address
/opt/default-sftproo-credentials.txt for the initial admin password.https://ip.add.re.ss:8443, sign in, and set a new password when prompted.sftproo is configured entirely through environment variables. The tables below group them by purpose. Variables marked required must be set for that feature to work; leaving them unset will either fail closed or fall back to an insecure default that the product will warn about in the logs.
| Variable | Purpose & why you set it | Default |
|---|---|---|
SFTPROO_HOST |
Bind address for the SFTP listener. Use 0.0.0.0 inside a container to accept connections on all interfaces. |
0.0.0.0 |
SFTPROO_PORT |
TCP port for SFTP. 2222 avoids clashing with the host's own SSH daemon on 22. | 2222 |
SFTPROO_WEB_PORT |
TCP port for the HTTPS admin UI. | 8443 |
sftproo stores all user, settings, license, and activity data in MariaDB / MySQL. These five variables are how the container finds the database at startup; if any of them is wrong the process aborts with a connection error.
| Variable | Purpose | Required |
|---|---|---|
SFTPROO_DB_HOST | Hostname or IP of the database server. Use the docker-compose service name (e.g. mariadb) when running both containers in the same stack. | Yes |
SFTPROO_DB_PORT | Database port. | Yes (typically 3306) |
SFTPROO_DB_NAME | Database / schema name. | Yes |
SFTPROO_DB_USER | Database user. | Yes |
SFTPROO_DB_PASSWORD | Password for the database user. | Yes |
| Variable | Purpose | Default |
|---|---|---|
SFTPROO_STORAGE_TYPE |
Where user files live. local is the only value supported at GA. S3 / Azure storage back-ends are on the roadmap and currently refuse to start. |
local |
SFTPROO_STORAGE_PATH |
Directory inside the container that holds per-user home directories. Mount a persistent volume here. | /data |
SFTPROO_CONFIG_PATH |
Directory for generated configuration and the license file. Mount a persistent volume here so the license and SSH host key survive restarts. | /etc/sftproo |
SSH_HOST_KEY_PATH |
Path to the SFTP server's host key. If the file does not exist at startup, sftproo generates one; the generated key must be kept between restarts so clients do not see host-key-changed warnings. | /etc/sftproo/ssh_host_key |
| Variable | Purpose | Default |
|---|---|---|
SFTPROO_CERT_FILE |
Path to the PEM-encoded TLS certificate for the admin UI. Mount a certificate from your CA or Let's Encrypt here; otherwise sftproo generates a self-signed cert that browsers will warn about. | /etc/sftproo/sftproo-server.crt |
SFTPROO_KEY_FILE |
Private key matching the certificate above. Keep this file readable only by the sftproo process. | /etc/sftproo/sftproo-server.pem |
| Variable | Purpose & why you set it | Default / behaviour when unset |
|---|---|---|
SFTPROO_SESSION_SECRET |
Signs the admin session cookie. Set this to a long, random, persistent value in production — when it is unset, sftproo generates a random one at every startup, which forces every admin to log in again on each restart. | Random, regenerated on each start. |
SFTPROO_ENCRYPTION_KEY |
Passphrase used to derive the key that encrypts stored cloud credentials (S3 secret keys, Azure account keys) in the database. Required to save S3 or Azure backup settings; the save call fails with a clear error when it is unset. Existing rows from older versions remain readable without it. | Empty — cloud backup settings cannot be saved until set. |
SFTPROO_DEFAULT_ADMIN_USER |
Username of the initial admin account created on first start. Only consulted on a truly empty database. | admin |
SFTPROO_DEFAULT_ADMIN_PASSWORD |
Initial password for the admin account. Leave unset to have sftproo generate a random password, print it once to the container logs, and force a password change on first login. Set an explicit value only when automation needs a known password. | Random, shown once in the logs. |
SFTPROO_DEFAULT_ADMIN_EMAIL |
Email recorded against the initial admin account. Does not send mail; used for display. | admin@example.com |
SFTPROO_SESSION_SECRET and SFTPROO_ENCRYPTION_KEY the same way you would a database root password: generate them once with openssl rand -hex 32, store them in your secret manager, and do not commit them to source control.
sftproo uses GORM as its database layer. Out of the box GORM can print every SQL statement it issues; that is great for debugging, noisy in production, and can leak parameter values into your log stream. This variable controls that verbosity.
| Variable | Purpose & why you set it | Default |
|---|---|---|
SFTPROO_LOG_LEVEL |
Controls database query logging. One of:
|
warn |
Example — temporarily turn on full query logging to diagnose a problem, then turn it back off:
# Crank it up
SFTPROO_LOG_LEVEL=info docker-compose up -d sftproo
docker-compose logs -f sftproo
# Restore the default (warn) when you are done
unset SFTPROO_LOG_LEVEL
docker-compose up -d sftproo
In a persistent deployment, set the value in your .env file or docker-compose.yml:
environment:
- SFTPROO_LOG_LEVEL=warn
On first start with a blank database, sftproo creates exactly one admin user. How it sets the password depends on whether SFTPROO_DEFAULT_ADMIN_PASSWORD is configured.
docker-compose up -d.docker-compose logs sftproo. Look for a block similar to:
Default admin user created with credentials:
Username: admin
Password: Hs5mPk3R-2pAu-fjVxQ9c7Yx
WARNING: This password was generated randomly and is shown only once.
You will be required to change it on first login.
https://your-server:8443 and log in with that username and password.The AMI / VM image writes the initial credentials to /opt/default-sftproo-credentials.txt. SSH into the instance, read the file, then log in and change the password as above.
Set SFTPROO_DEFAULT_ADMIN_PASSWORD explicitly only when you are bootstrapping sftproo from Ansible / Terraform and cannot fish a password out of logs. In that case the forced-change flow is skipped, so choose a strong value and rotate it afterwards using the web UI.
admin123. If you are upgrading from an earlier release and that applies to you, the next login will redirect straight to the change-password page.
To create a new user from the web UI:
Every user can authenticate with a password, an SSH public key, or both. To manage keys:
Flipping the Active toggle off blocks the user from both SFTP and the web UI without deleting their home directory, so their files remain available if you need them for audit or later restoration.
SFTPROO_CONFIG_PATH)SFTPROO_STORAGE_PATH)Backup encryption is a Professional feature. Each backup is sealed with AES-256-GCM under a key derived from your passphrase with argon2id and a per-backup random salt. A few things to know:
decrypt_backup tool auto-detects the file format.If a backup is encrypted, first decrypt it with the bundled decrypt_backup tool (shipped inside the container at /app/tools/decrypt_backup):
decrypt_backup \
-in /backups/sftproo-2025-01-15.tar.gz.enc \
-out /tmp/sftproo-2025-01-15.tar.gz \
-password 'your-backup-passphrase'
Then stop the running service, extract the archive over the matching volumes, and start it again:
docker-compose stop sftproo
tar -xzf /tmp/sftproo-2025-01-15.tar.gz -C /opt/sftproo
docker-compose start sftproo
Restrict who can reach sftproo by source IP. Go to Settings → Security → ACL to add IPv4 addresses or CIDR ranges, and toggle whitelist mode on to deny everything else. The host's own IPs are added automatically so you cannot accidentally lock out the server.
By default sftproo generates a self-signed certificate on first start. For production:
Option 1 — Let's Encrypt in front of sftproo: terminate TLS at a reverse proxy (Caddy, Traefik, nginx) and forward to 8443 over the container network.
Option 2 — Certificate files (recommended for fully self-contained deployments):
volumes:
- ./certs/server.crt:/etc/sftproo/sftproo-server.crt:ro
- ./certs/server.key:/etc/sftproo/sftproo-server.pem:ro
sftproo automatically flags any admin that is still using the old admin123 default, as well as the initial admin created from the random bootstrap password. Flagged accounts cannot reach any other page until they set a new password of at least 12 characters.
sftproo records:
View them under Settings → Activity Logs or tail the container logs:
docker logs -f sftproo
SFTPROO_SESSION_SECRET and SFTPROO_ENCRYPTION_KEY to long random values and store them in a secret manager.Welcome! This section is for people who have an sftproo account and want to move files in or out.
2222 (not the standard SSH port 22).https://server.example.com:8443, where you can change your own password and manage your SSH keys.From the command line:
sftp -P 2222 username@server.example.com
You will be asked to accept the server's host key the first time; type yes to continue. You can also use any of the graphical clients listed in SFTP Clients — they often make things easier.
Log in at https://server.example.com:8443, open Change Password from the account menu in the top right, enter your current and new passwords, and save. If you were handed a temporary password by your admin, you may be redirected here automatically before anything else.
Once connected, use these commands to browse and transfer files:
| Command | Description | Example |
|---|---|---|
ls | List files in the current directory | ls -la |
cd | Change directory | cd documents |
pwd | Show the current directory | pwd |
mkdir | Create a directory | mkdir newfolder |
put | Upload a file | put localfile.txt |
get | Download a file | get remotefile.txt |
rm | Delete a file | rm oldfile.txt |
rmdir | Delete an empty directory | rmdir oldfolder |
rename | Rename a file or directory | rename old.txt new.txt |
# Upload all .pdf files
put *.pdf
# Download a whole directory
get -r documents/
# Upload a whole directory
put -r local_folder/
# Resume an upload
reput largefile.zip
# Resume a download
reget largefile.zip
Linux / macOS:
ssh-keygen -t ed25519 -f ~/.ssh/sftproo_key -C "your_email@example.com"
Windows (PowerShell):
ssh-keygen -t ed25519 -f $HOME\.ssh\sftproo_key -C "your_email@example.com"
Option 1 — via the web UI:
sftproo_key.pub, give it a descriptive name, and save.Option 2 — ask your administrator to add the public key for you.
sftp -P 2222 -i ~/.ssh/sftproo_key username@server.example.com
Avoid typing -i every time by loading the key into your SSH agent:
eval $(ssh-agent)
ssh-add ~/.ssh/sftproo_key
# Now connect without specifying the key
sftp -P 2222 username@server.example.com
Graphical clients are usually friendlier than the command line. These all speak standard SFTP and work with sftproo out of the box.
2222, and supply your username / password. To use a key, go to Advanced → SSH → Authentication.sftp://server.example.com as the host and set the port to 2222.2222, and attach your key if needed.sftp://username@server:2222.2222, not the OS's SSH port 22.../ outside that root are rejected.ls -la before trying to overwrite.iperf3 if possible — SFTP cannot go faster than the network beneath it.reput / reget to resume rather than repeat.reput / reget to resume instead of restarting.sftproo is free to run for SFTP and local storage. A Professional license unlocks the paid features below. Licenses are cryptographically signed and validated locally — no call-home is required.
| Feature | What it does | Plan |
|---|---|---|
| Backup to S3 | Ship scheduled and manual backups to an AWS S3 bucket instead of (or in addition to) local disk. | Professional |
| Backup to Azure | Ship backups to an Azure Blob Storage container. | Professional |
| Encrypted backups | Seal each backup with AES-256-GCM under a passphrase you choose; the backup is unreadable without it, even if the storage bucket is compromised. | Professional |
| 24/7 support | Priority email and ticket response from the sftproo team. | Professional |
If you try to enable a licensed feature on a free deployment, sftproo rejects the change and records the attempt in the activity log.
There are two routes to a Professional license:
Launching sftproo from the AWS Marketplace or Azure Marketplace includes a Professional license that is applied on first boot. Billing is metered through your cloud account — no separate invoice from us. This is the simplest path if your organisation already procures software through AWS or Azure.
If you self-host on your own hardware, in a different cloud, or in an air-gapped environment, you can buy a license directly from us:
Once you have a license key, apply it one of two ways.
If the web UI is not reachable, or you want the license in place before the process starts:
# Drop the key into the config volume
echo 'YOUR-BASE32-LICENSE-KEY' > /etc/sftproo/license_key.txt
chmod 600 /etc/sftproo/license_key.txt
# Restart the container so it picks it up
docker-compose restart sftproo
On startup, sftproo loads the file, verifies the signature against the compiled-in public key, and enables the licensed features.
Uploading a new key from the License page overwrites the previous one. The activity log records every license change so you can audit when a feature set changed and who changed it.