Most FTP servers aren't migrated because someone planned it. They're migrated because the hosting contract is ending, a security review flagged plaintext credentials, or the disk is full. This guide is the plan for doing it properly: move every file off the FTP server to cloud storage (or to SFTP), prove nothing was lost, and cut over the people and systems that still depend on it.
The short version: inventory the server, pick a destination, run a dry run, copy in folder-sized batches with "skip existing" enabled, verify counts and bytes per folder, run a final delta pass after freezing writes, and keep the FTP server read-only for 30 days before you decommission it.
Why teams move off FTP
FTP still works. That's the problem — it keeps working long after the reasons to leave have piled up:
- It's unencrypted. FTP sends the username, password, and every file byte in plaintext. Anyone on the network path can read them.
- It's one server. No replication, no versioning, no durability guarantees beyond whatever RAID and backups someone set up years ago.
- It's operational drag. OS patching, disk capacity, and firewall rules for the passive-mode port range are all yours to maintain.
- Modern consumers don't read from FTP. CDNs, apps, and data pipelines expect object storage or an HTTPS endpoint.
Step 1 — Choose the destination
Where the files should land depends on who reads them afterwards:
| Destination | Best for | Keep in mind |
|---|---|---|
| Amazon S3 | Files served to apps or through a CDN (CloudFront) | Folders become key prefixes; access control moves to IAM and bucket policies |
| Google Cloud Storage | Workloads already on GCP | Same object-storage model as S3 |
| Azure Blob Storage | Microsoft-centric environments | Same object-storage model; files live in containers |
| SFTP server | Partners who still need a file-drop endpoint | Encrypted drop-in replacement, but still a server to run |
| Google Drive / Dropbox | Business users browsing files by hand | API rate limits; not built for application delivery |
Univaultport can use any of these as the destination for an FTP source, and you can split one FTP server across several — for example, public media to S3 and partner drop folders to SFTP.
Step 2 — Inventory the FTP server
Before copying anything, know what you're moving. You want four numbers: file count, total bytes, largest file, and the list of top-level folders.
If you have shell access to the server itself, this is quick:
# Total size and file count under the FTP root
du -sh /srv/ftp
find /srv/ftp -type f | wc -l
# The ten largest files
find /srv/ftp -type f -printf '%s %p\n' | sort -nr | head -10
# Size per top-level folder — these become your migration batches
du -sh /srv/ftp/*If you only have FTP access, lftp can report sizes from the client side:
lftp -u migrator ftp.example.com -e "du -sh /pub; bye"While you're there, answer two more questions:
- Who writes to this server? Check the transfer log (
xferlogon vsftpd and ProFTPD) for upload activity over the last 90 days. Every account that uploads is someone you'll need to cut over. - What's unusual? Symbolic links, filenames in legacy encodings (common on old Windows servers), and names with characters like
#,?, or+that behave badly in URLs once the files are served over HTTP.
Step 3 — Map the folder structure
FTP has real directories. Object storage doesn't — it has keys, and the "folders" you see in the console are just shared key prefixes. The mapping is still one-to-one:
ftp://ftp.example.com/pub/assets/2024/banner.jpg
→ s3://acme-assets/Univaultport/pub/assets/2024/banner.jpgBy default, Univaultport keeps the full source hierarchy and places it under a top-level Univaultport/ folder at the destination, so migrated files are easy to tell apart. You can choose your own destination folder instead (for example ftp-archive/); the hierarchy beneath it is preserved either way.
Two things don't carry over cleanly, and you should decide what to do about them before the copy:
- Permissions. POSIX permissions (
rwxr-xr-x) have no equivalent in S3, GCS, or Azure. Re-express who-can-read-what as IAM roles and bucket or container policies. - Timestamps. Object stores set their own last-modified time when a file is written. If downstream systems sort or filter by the original modification date, capture it from your inventory first.
Step 4 — Connect and run a dry run
Connect the FTP server as a source (FTP setup guide) and your destination — for example S3, Google Cloud Storage, Azure Blob, or SFTP. Credentials are encrypted at rest and never returned in API responses.
Then start the migration as a dry run. A dry run lists every file and opens each one for reading, but writes nothing to the destination. It catches the problems you want to find now rather than halfway through: folders the FTP account can't read, files that fail to download, and a file count that doesn't match your inventory.
Step 5 — Copy in folder-sized batches
Don't migrate the whole server as one job. Run one job per top-level folder (the batches from your du -sh /srv/ftp/* output):
- A failure is isolated to one folder, and re-running it is cheap.
- You can verify and sign off each folder as it completes.
- Folders that are still being written to can go last.
FTP handles one command at a time per connection, so throughput is dominated by network latency rather than bandwidth. Thousands of small files take longer than their size suggests — plan on roughly 50–200 seconds per 1,000 small files at typical latencies — while large files move as fast as the link allows. Univaultport streams each file directly from the FTP server to the destination; nothing is staged on an intermediate disk.
Set the conflict policy to Skip (the default). With Skip, files that already exist at the destination are left alone, which makes every job safe to re-run: if a batch fails partway, run it again and it picks up only what's missing.
Step 6 — Verify every folder
A job that finished isn't the same as a migration that's correct. For each folder, compare file count and total bytes between source and destination.
# S3 — object count and total size under the migrated prefix
aws s3 ls s3://acme-assets/Univaultport/pub/assets/ --recursive --summarize | tail -2
# Google Cloud Storage — total size under the prefix
gcloud storage du --summarize gs://acme-assets/Univaultport/pub/assets/Compare those against the per-folder numbers from Step 2. Then spot-check content: download a random sample of files from both sides and compare checksums with sha256sum. Most FTP servers don't expose a standard checksum command, so a sampled comparison is the practical check. The job's per-asset log records every file transferred and any that failed, so you know exactly which files to re-run.
Step 7 — Delta sync and cutover
Unless the FTP server is already frozen, files keep arriving while you migrate. Handle it in this order:
Delta passes
Re-run each folder's job (or schedule it daily) with Skip enabled. Each pass copies only files that don't exist at the destination yet.Move the writers
Point partners, cron jobs, and CMS exports at the new destination — an SFTP endpoint, S3 presigned uploads, or whatever you chose in Step 1.Freeze the FTP server
Make it read-only (on vsftpd, setwrite_enable=NO) so nothing new lands there.Final pass and verify
Run one last delta pass, then repeat the Step 6 checks. Counts and bytes should match exactly.Keep it read-only for 30 days
Leave the server running read-only before you decommission it. The cost is trivial compared with discovering a missed folder after the disk is wiped.
Common FTP migration problems
| Symptom | Likely cause | Fix |
|---|---|---|
| Directory listing times out | The firewall blocks the passive-mode data ports | Open the server's passive port range (pasv_min_port / pasv_max_port on vsftpd, PassivePorts on ProFTPD) |
530 login failure | Wrong username or password | Confirm the credentials with FileZilla or the ftp CLI first |
550 on some folders only | Per-directory permissions for the FTP account | Grant the migration account read access to every folder in scope |
| Fewer files at the destination than in the inventory | Symbolic links, which are skipped | Resolve links to real files at the source, or migrate the link targets directly |
| Garbled filenames | Legacy (non-UTF-8) filename encoding | Rename files to UTF-8 at the source before migrating |
| Migration slower than expected | Many small files over a high-latency link | Split into more folder batches and schedule the large ones overnight |
Frequently asked questions
Can I migrate an FTP server to S3 without downloading everything first?
Yes. Univaultport streams each file from the FTP server straight into S3 (or GCS, Azure Blob, SFTP, and other destinations). There's no local copy or staging disk in between.
Does the folder structure survive the migration?
Yes. The full directory hierarchy is recreated at the destination — as key prefixes in object storage, or as real folders on SFTP, Google Drive, and Dropbox.
Does Univaultport support FTPS (FTP over TLS)?
Not currently. The FTP connector supports plain FTP in passive mode. If your server also offers SFTP, use the SFTP connector for an encrypted transfer.
Should I migrate to SFTP or to cloud storage?
If external partners need somewhere to drop files, SFTP is the least disruptive replacement — same workflow, encrypted. If the files are consumed by applications, websites, or a CDN, object storage like S3 is the better long-term home. Many teams do both.
How long does an FTP server migration take?
File count matters more than total size. Large files transfer at the speed of your link; thousands of small files are limited by per-file round trips. Your dry run in Step 4 is the best estimate for your specific server.
Next steps
- Connect your FTP server — host, port, passive mode, and root path
- Enterprise migration best practices — dry runs, idempotency, chunking, and cutover in depth
- Every supported connector — all the sources and destinations Univaultport moves files between



