Need help with ftpd?
Click the “chat” button below for chat support from the developer who created it, or find similar developers for support.

About the developer

130 Stars 31 Forks Other 608 Commits 11 Opened issues


Ftpd is a pure Ruby FTP server library. It supports implicit and explicit TLS, IPV6, passive and active mode, and is unconditionally compliant per RFC-1123. It can be used as part of a test fixture or embedded in a program.

Services available


Need anything else?

Contributors list

# 435,494
385 commits
# 133,084
11 commits
# 146,401
5 commits
# 115,008
5 commits
# 230,423
2 commits
# 142,504
1 commit
# 204,648
1 commit
# 232,292
1 commit
# 296,077
1 commit


Gem Version Dependency Status Build Status Code Climate

ftpd is a pure Ruby FTP server library. It supports implicit and explicit TLS, IPV6, passive and active mode, and is unconditionally compliant per RFC-1123. It can be used as part of a test fixture or embedded in a program.

A note about this README

This readme contains Yardoc markup for links to the API docs; those links don't display properly on github. You'll find a properly rendered version on

The state of this library

Ftpd has been used for many years to test FTP clients, and is stable and reliable for that purpose. However, it was not originally intended to be part of a publically accessible FTP server. I would be cautious in using it in an untrusted environment due to the probability that it contains critical flaws (or even security vulnarabilities) that have not been discovered in its use as a test harness.

Hello World

This is examples/hello_world.rb, a bare minimum FTP server. It allows any user/password, and serves files in a temporary directory. It binds to an ephemeral port on the local interface:

require 'ftpd'
require 'tmpdir'

class Driver

def initialize(temp_dir) @temp_dir = temp_dir end

def authenticate(user, password) true end

def file_system(user) end


Dir.mktmpdir do |temp_dir| driver = server = server.start puts "Server listening on port #{server.bound_port}" gets end

A more full-featured example that allows TLS and takes options is in examples/example.rb


Ftpd's dynamic behavior such as authentication and file retrieval is controlled by a driver that you supply. The Driver class in the "hello world" example above shows a rudimentary driver. Ftpd calls the authenticate method to decide who can log in. Once someone is logged on, it calls the file_system method to obtain a file system driver for that user.

There is no base class for a driver. Any object that quacks like a driver will do. Here are the methods your driver needs:

  • {Example::Driver#authenticate authenticate}
  • {Example::Driver#filesystem filesystem}

File System

The file system object that the driver supplies to Ftpd is Ftpd's gateway to the logical file system. Ftpd doesn't know or care whether it's serving files from disk, memory, or any other means.

The file system can be very minimal. If the file system is missing certain methods, the server simply disables the commands which need that method. For example, if there is no write method, then STOR is not supported and causes a "502 Command not implemented" response to the client.

The canonical and commented example of an Ftpd file system is {Ftpd::DiskFileSystem}. You can use it as a template for creating your own, and its comments are the official specification for an Ftpd file system.

Here are the methods a file system may expose:

  • {Ftpd::DiskFileSystem::Accessors#accessible? accessible?}
  • {Ftpd::DiskFileSystem::Accessors#directory? directory?}
  • {Ftpd::DiskFileSystem::Accessors#exists? exists?}
  • {Ftpd::DiskFileSystem::Append#append append}
  • {Ftpd::DiskFileSystem::Delete#delete delete}
  • {Ftpd::DiskFileSystem::List#dir dir}
  • {Ftpd::DiskFileSystem::List#fileinfo fileinfo}
  • {Ftpd::DiskFileSystem::Mkdir#mkdir mkdir}
  • {Ftpd::DiskFileSystem::Read#read read}
  • {Ftpd::DiskFileSystem::Rename#rename rename}
  • {Ftpd::DiskFileSystem::Rmdir#rmdir rmdir}
  • {Ftpd::DiskFileSystem::Write#write write}


Ftpd includes a disk based file system:

class Driver


def file_system(user)'/var/lib/ftp') end


Warning: The DiskFileSystem allows file and directory modification including writing, renaming, deleting, etc. If you want a read-only file system, then use {Ftpd::ReadOnlyDiskFileSystem} instead.

The DiskFileSystem is composed out of modules:

  • {Ftpd::DiskFileSystem::Base Base} - You will need this
  • {Ftpd::DiskFileSystem::Append Append} - File appending
  • {Ftpd::DiskFileSystem::Delete Delete} - File deletion
  • {Ftpd::DiskFileSystem::List List} - Directory listing
  • {Ftpd::DiskFileSystem::Mkdir Mkdir} - Directory creation
  • {Ftpd::DiskFileSystem::Read Read} - File reading
  • {Ftpd::DiskFileSystem::Rename Rename} - File renaming
  • {Ftpd::DiskFileSystem::Rmdir Rmdir} - Directory removal
  • {Ftpd::DiskFileSystem::Write Write} - File writing

You can use these modules to create a custom disk file system that allows only the operations you want, or which mixes the predefined modules with your customizations, as in this silly example that allows uploads but then throws them away.

class BlackHole
  def write(ftp_path, contents)

class CustomDiskFileSystem include DiskFileSystem::Base include DiskFileSystem::Read include BlackHole end


Configuration is done via accessors on {Ftpd::FtpServer}. For example, to set the session timeout to 10 minutes:

server =
server.session_timeout = 10 * 60

You can set any of these attributes before starting the server:

  • {Ftpd::FtpServer#nat_ip}
  • {Ftpd::FtpServer#passive_ports}
  • {Ftpd::FtpServer#allowlowdata_ports}
  • {Ftpd::FtpServer#auth_level}
  • {Ftpd::FtpServer#exception_handler}
  • {Ftpd::FtpServer#failedlogindelay}
  • {Ftpd::FtpServer#list_formatter}
  • {Ftpd::FtpServer#log}
  • {Ftpd::FtpServer#maxconnectionsper_ip}
  • {Ftpd::FtpServer#max_connections}
  • {Ftpd::FtpServer#maxfailedlogins}
  • {Ftpd::FtpServer#response_delay}
  • {Ftpd::FtpServer#server_name}
  • {Ftpd::FtpServer#server_version}
  • {Ftpd::FtpServer#session_timeout}
  • {Ftpd::Server#interface}
  • {Ftpd::Server#port}
  • {Ftpd::TlsServer#certfile_path}
  • {Ftpd::TlsServer#tls}

LIST output format

By default, the LIST command uses Unix "ls -l" formatting:

-rw-r--r-- 1 user     group        1234 Mar  3 08:38 foo

An alternative to "ls -l" formatting is Easily Parsed LIST format (EPLF) format:


to configure Ftpd for EPLF formatting:

ftp_server.list_formatter = Ftpd::ListFormat::Eplf

To create your own custom formatter, create a class with these methods:

  • {Ftpd::ListFormat::Ls#initialize initialize}
  • {Ftpd::ListFormat::Ls#tos tos}

And register your class with the ftp_server before starting it:

ftp_server.list_formatter = MyListFormatter


Ftpd can write to an instance of { Logger} that you provide. To log to standard out:

server.log =$stdout)

To log to a file:

server.log ='/tmp/ftpd.log')

Standards Compliance

  • Unconditionally compliant per RFC-1123 (Requirements for Internet Hosts).

  • Implements all of the security recommendations in RFC-2577 (FTP Security Considerations).

  • Implements RFC-2389 (Feature negotiation mechanism for the File Transfer Protocol)

  • Implements RFC-2428 (FTP Extensions for IPv6 and NATs)

  • Implements enough of RFC-4217 (Securing FTP with TLS) to get by.

See RFC Compliance for details

Ruby Compatability

The tests pass with these Rubies:

  • ruby-1.9.3
  • ruby-2.0
  • ruby-2.1
  • ruby-2.2
  • ruby-2.3
  • ruby-2.4 (but see below)

For Ruby 1.8, use an ftpd version before 0.8. In your Gemfile:

gem 'ftpd', '<0.8'

This gem runs fine in Ruby 2.4, but the tests that use TLS are skipped in Ruby 2.4. That is because the doublebagftps gem that the tests use for TLS does not work in Ruby 2.4.

OS compatability


Ftpd runs on:

  • Linux
  • OSX


The master branch of ftpd does not currently run on Windows. There is an experimental branch for Windows which contains several changes that ought to make ftpd work on Windows, but they need testing. To try that branch, use this line in your Gemfile:

gem 'ftpd', github: 'wconrad/ftpd', branch: 'windows'

Does it work for you? Is it busted? Please report your experience here.


This library uses Semantic Versioning. Ftpd promises not to make breaking changes to its API without bumping the major version.



To run the cucumber (functional) tests:

$ rake test:features

To run the rspec (unit) tests:

$ rake test:spec

To run all tests:

$ rake test

or just:

$ rake

To force features to write the server log to stdout:

$ FTPD_DEBUG=1 rake test:features


The stand-alone example is good for manually testing Ftpd with any FTP client. To run the stand-alone example:

$ examples/example.rb

The example prints its port, username and password to the console. You can connect to the stand-alone example with any FTP client.

example.rb has many options. To see them:

$ examples/example.rb -h


  1. Fork it
  2. Create your feature branch (
    git checkout -b my-new-feature
  3. Commit your changes (
    git commit -am 'Add some feature'
  4. Push to the branch (
    git push origin my-new-feature
  5. Create new Pull Request


I created ftpd to support the test framework I wrote for Databill, LLC, which has given its kind permission to donate it to the community.


Wayne Conrad [email protected]


Thanks to Databill, LLC, which supported the creation of this library, and granted permission to donate it to the community.


Among those who have improved ftpd are:

  • Alfonso Cora
  • André Aizim Kelmanson
  • Bjoern B. Dorra
  • Joshua Rutherford
  • Larry. W. Cashdollar
  • Michael de Silva
  • Mike Ragalie
  • cransom

If I've forgotten to add you, please remind me, or submit a merge request.

Thank you!

See also

We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.