Portainer Templates logo

Portainer Templates

Pure-FTP Server Pure-FTP Server

Container

Network OtherUtilities

Pure-FTPd is a free (BSD), secure, production-quality and standard-conformant FTP server.

Image details

Pulls: 40.1M
Architecture: amd64
Image size: 59 MB
User: stilliard
Created: May 29, 2014
Updated: 1 day ago
Status: active

Configuration

Type
Container
Image
stilliard/pure-ftpd:hardened
Ports
21/tcp30000/tcp30001/tcp30002/tcp30003/tcp30004/tcp30005/tcp30006/tcp30007/tcp30008/tcp30009/tcp
Volumes
/etc/pure-ftpd/passwd : /opt/mediadepot/apps/pureftp/data/tvshows : /media/storage/tvshows/data/movies : /media/storage/movies/data/music : /media/storage/music/data/ebooks : /media/storage/ebooks/data/photos : /media/storage/photos/data/documents : /media/storage/documents/data/downloads : /media/storage/downloads/data/software : /media/storage/software/data/blackhole : /media/temp/blackhole/data/processing : /media/temp/processing
Env vars
FTP_USER_NAME=depotFTP_USER_PASS=badpassFTP_USER_HOME=/dataFTP_USER_GID=15000FTP_USER_UID=15000TZ=America/Los_AngelesPUBLICHOST=localhost

Template by mediadepot

Standalone Install

Select an install method, to see config/commands for deploying Pure-FTP Server

Installation method

Install on Portainer

Import all app templates into your Portainer instance, for easy 1-click deploys

  1. Ensure both Docker and Portainer are installed, and up-to-date
  2. Log into your Portainer web UI
  3. Under Settings → App Templates, paste the below URL
  4. Head to Home → App Templates, and the list of apps will show up
  5. Select Pure-FTP Server, fill in any config options, and hit Deploy

Template Import URL

https://raw.githubusercontent.com/Lissy93/portainer-templates/main/templates.json
Show Me demo

More install options in our documentation.

Docker Pure-ftpd Server

https://hub.docker.com/r/stilliard/pure-ftpd/
Docker Automated build Docker Pulls FOSSA Status Sponsor Project

Check out our basic example workflow & our slightly more advanced workflow with tls & an auto created user.

----------------------------------------
Pull down latest version with docker:
docker pull stilliard/pure-ftpd

If you get a permissions error, you may need to add your user to the docker group, or run with sudo
If you want to make changes, my advice is to either change the run command when running it or extend this image to make any changes rather than forking the project.
This is because rebuilding the entire docker image via a fork can be very slow as it rebuilds the entire pure-ftpd package from source.
To change the command run on start you could use the command: option if using docker-compose, or with docker run directly you could use:
docker run --rm -d --name ftpd_server -p 21:21 -p 30000-30009:30000-30009 stilliard/pure-ftpd bash /run.sh -c 30 -C 10 -l puredb:/etc/pure-ftpd/pureftpd.pdb -E -j -R -P localhost -p 30000:30059

To extend it you can create a new project with a DOCKERFILE like so:
FROM stilliard/pure-ftpd

# e.g. you could change the defult command run:
CMD /run.sh -c 30 -C 10 -l puredb:/etc/pure-ftpd/pureftpd.pdb -E -j -R -P $PUBLICHOST -p 30000:30059

Then you can build your own image, docker build --rm -t my-pure-ftp ., where my-pure-ftp is the name you want to build as

Starting it

docker run -d --name ftpd_server -p 21:21 -p 30000-30009:30000-30009 -e "PUBLICHOST=localhost" stilliard/pure-ftpd
Or for your own image, replace stilliard/pure-ftpd with the name you built it with, e.g. my-pure-ftp
You can also pass ADDEDFLAGS as an env variable to add additional options such as --tls to the pure-ftpd command.
e.g. -e "ADDED_FLAGS=--tls=2"

Operating it

docker exec -it ftpd_server /bin/bash

Setting runtime FTP user

To create a user on the ftp container, use the following environment variables: FTP_USER_NAME, FTP_USER_PASS and FTP_USER_HOME.
FTP_USER_HOME is the root directory of the new user.
Example usage:
docker run -e FTP_USER_NAME=bob -e FTP_USER_PASS=12345 -e FTP_USER_HOME=/home/bob stilliard/pure-ftpd
If you wish to set the UID & GID of the FTP user, use the FTP_USER_UID & FTP_USER_GID environment variables.

Using different passive ports

To use passive ports in a different range (eg: 10000-10009), use the following setup:
docker run -e FTP_PASSIVE_PORTS=10000:10009 --expose=10000-10009 -p 21:21 -p 10000-10009:10000-10009
You may need the --expose= option, because default passive ports exposed are 30000 to 30009.

Example usage once inside

Create an ftp user: e.g. bob with chroot access only to /home/ftpusers/bob
pure-pw useradd bob -f /etc/pure-ftpd/passwd/pureftpd.passwd -m -u ftpuser -d /home/ftpusers/bob
No restart should be needed.
If you have any trouble with volume permissions due to the uid or gid of the created user you can change the -u flag for the uid you would like to use and/or specify -g with the group id as well. For more information see issue #35
.
More info on usage here: https://download.pureftpd.org/pure-ftpd/doc/README.Virtual-Users

Test your connection

From the host machine:
ftp -p localhost 21

Docker compose

Docker compose can help you simplify the orchestration of your containers.
We have a simple example of the docker compose
.
& here's a more detailed example using wordpress with ftp using this image.

Max clients

By default we set 5 max clients at once, but you can increase this by using the following environment variable FTP_MAX_CLIENTS, e.g. to FTP_MAX_CLIENTS=50 and then also increasing the number of public ports opened from FTP_PASSIVE_PORTS=30000:30009 FTP_PASSIVE_PORTS=30000:30099. You'll also want to open those ports when running docker run. In addition you can specify the maximum connections per ip by setting the environment variable FTP_MAX_CONNECTIONS. By default the value is 5.

All Pure-ftpd flags available:

https://linux.die.net/man/8/pure-ftpd

Logs

To get verbose logs add the following to your docker run command:
-e "ADDED_FLAGS=-d -d"

Then the logs will be redirected to the stdout of the container and captured by the docker log collector. You can watch them with docker logs -f ftpd_server
Or, if you exec into the container you could watch over the log with tail -f /var/log/messages
Want a transfer log file? add the following to your docker run command:
-e "ADDED_FLAGS=-O w3c:/var/log/pure-ftpd/transfer.log"

Tags available for different versions

Tags are named after Debian releases.
  • latest - latest version, currently Debian Trixie
  • trixie-latest - latest pure-ftpd on Debian Trixie (current stable)
  • bookworm-latest - latest pure-ftpd on Debian Bookworm
  • bullseye-latest - latest pure-ftpd on Debian Bullseye
  • trixie-1.x.x / bookworm-1.x.x / bullseye-1.x.x - pinned pure-ftpd version on a specific distro

End of life / legacy tags (no longer updated)
  • buster-1.0.47 - final Debian Buster build (Buster is EOL)
  • stretch-latest, jessie-latest, wheezy-1.0.36 - older Debian releases, kept for reference

Check the tags on Docker Hub for all available versions
Usage of specific tags: docker pull stilliard/pure-ftpd:trixie-latest
An arm64 build is also available here: https://hub.docker.com/r/zhabba/pure-ftpd-arm64
- Thanks @zhabba

Our default pure-ftpd options explained

/usr/sbin/pure-ftpd # path to pure-ftpd executable
-c 5 # --maxclientsnumber (no more than 5 people at once)
-C 5 # --maxclientsperip (no more than 5 requests from the same ip)
-l puredb:/etc/pure-ftpd/pureftpd.pdb # --login (login file for virtual users)
-E # --noanonymous (only real users)
-j # --createhomedir (auto create home directory if it doesnt already exist)
-R # --nochmod (prevent usage of the CHMOD command)
-P $PUBLICHOST # IP/Host setting for PASV support, passed in your the PUBLICHOST env var
-p 30000:30009 # PASV port range (10 ports for 5 max clients)
-tls 1 # Enables optional TLS support

For more information please see man pure-ftpd, or visit: https://www.pureftpd.org/

Why so many ports opened?

This is for PASV support, please see: #5 PASV not fun :)

Docker Volumes

There are a few spots onto which you can mount a docker volume to configure the server and persist uploaded data. It's recommended to use them in production.
- /home/ftpusers/ The ftp's data volume (by convention). - /etc/pure-ftpd/passwd A directory containing the single pureftpd.passwd
file which contains the user database (i.e., all virtual users, their
passwords and their home directories). This is read on startup of the
container and updated by the `pure-pw useradd -f /etc/pure-
ftpd/passwd/pureftpd.passwd ...` command.
- /etc/ssl/private/ A directory containing a single pure-ftpd.pem file
with the server's SSL certificates for TLS support. Optional TLS is
automatically enabled when the container finds this file on startup.

Keep user database in a volume

You may want to keep your user database through the successive image builds. It is possible with Docker volumes.
Create a named volume:
docker volume create --name my-db-volume

Specify it when running the container:
docker run -d --name ftpd_server -p 21:21 -p 30000-30009:30000-30009 -e "PUBLICHOST=localhost" -v my-db-volume:/etc/pure-ftpd/passwd stilliard/pure-ftpd

When an user is added, you need to use the password file which is in the volume:
pure-pw useradd bob -f /etc/pure-ftpd/passwd/pureftpd.passwd -m -u ftpuser -d /home/ftpusers/bob
(Thanks to the -m option, you don't need to call pure-pw mkdb with this syntax).

Changing a password

e.g. to change the password for user "bob":
pure-pw passwd bob -f /etc/pure-ftpd/passwd/pureftpd.passwd -m

Development (via git clone)
# Clone the repo
git clone https://github.com/stilliard/docker-pure-ftpd.git
cd docker-pure-ftpd
# Build the image
make build
# Run container in background:
make run
# enter a bash shell inside the container:
make enter
# test that it's all working with
make test

TLS

If you want to enable tls (for ftps connections), you need to have a valid certificate. You can get one from one of the certificate authorities that you'll find when googling this topic. The certificate (containing private key and certificate) needs to be at:
/etc/ssl/private/pure-ftpd.pem

Use docker volumes to get the certificate there at runtime. The container will automatically enable optional TLS when it detect the file at this location.
You can also self-sign a certificate, which is certainly the easiest way to start out. Self signed certificates come with certain drawbacks, but it might be better to have a self signed one than none at all.
Here's how to create a self-signed certificate from within the container:
mkdir -p /etc/ssl/private
openssl dhparam -out /etc/ssl/private/pure-ftpd-dhparams.pem 2048
openssl req -x509 -nodes -newkey rsa:2048 -sha256 -keyout \
    /etc/ssl/private/pure-ftpd.pem \
    -out /etc/ssl/private/pure-ftpd.pem
chmod 600 /etc/ssl/private/*.pem

Automatic TLS certificate generation

If ADDED_FLAGS contains --tls (e.g. --tls=1 or --tls=2) and file /etc/ssl/private/pure-ftpd.pem does not exists it is possible to generate self-signed certificate if TLS_CN, TLS_ORG and TLS_C are set.
Keep in mind that if no volume is set for /etc/ssl/private/ directory generated certificates won't be persisted and new ones will be generated on each start.
You can also pass -e "TLS_USE_DSAPRAM=true" for faster generated certificates though this option is not recommended for production.
Please check out the TLS docs here.

TLS with cert and key file for Let's Encrypt

Let's Encrypt provides two separate files for certificate and keyfile. The Pure-FTPd TLS encryption documentation suggests to simply concat them into one file. So you can simply provide the Let's Encrypt cert `/etc/ssl/private/pure-ftpd-cert.pem and key /etc/ssl/private/pure-ftpd-key.pem via Docker Volumes and let them get auto-concatenated into /etc/ssl/private/pure-ftpd.pem`. Or concat them manually with
cat /etc/letsencrypt/live/<your_server>/cert.pem /etc/letsencrypt/live/<your_server>/privkey.pem > pure-ftpd.pem

Credits

Thanks for the help on stackoverflow with this! https://stackoverflow.com/questions/23930167/installing-pure-ftpd-in-docker-debian-wheezy-error-421
Also thanks to all the awesome contributors that have made this project amazing! https://github.com/stilliard/docker-pure-ftpd/graphs/contributors
You can also help support the development of this project with coffee power: Buy Me A Coffee

License

FOSSA Status

Serve Pure-FTP Server on your own domain behind Caddy, Nginx or Traefik. Fill in your domain and copy the result. It's a starting point, some apps need their own base URL or extra headers set too.

Proxying pure-ftp-server.example.com to http://localhost:21

Add this to your Caddyfile

pure-ftp-server.example.com {
	reverse_proxy http://localhost:21
}

Check the logs first

Nine times out of ten the logs tell you exactly what went wrong.

  • In Portainer, go to Containers, click the container, then Logs. Or run docker logs pureftp
  • Exit codes help too: 137 means killed, usually out of memory. 126 or 127 means the command inside the image is broken.

Runs on the host network

This container shares your server's network directly, so port mappings are ignored and every port the app opens binds straight to the host.

  • If a port won't bind, find the clash with sudo ss -tlnp and stop the other service, or change the port in pureftp's own settings.

Permission denied on volumes

If the logs show "permission denied", the app can't write to its data folder on the host.

  • Fix the ownership: sudo chown -R 1000:1000 /opt/mediadepot/apps/pureftp (and the same for the other mapped folders)

Image won't pull

Test the pull directly on the host: docker pull stilliard/pure-ftpd:hardened

  • "manifest unknown" means the tag no longer exists.
  • "toomanyrequests" is the Docker Hub rate limit. Log in with docker login to raise it.
  • "no space left on device" means a full disk. Reclaim space with docker system prune

"exec format error"

This means the image was built for a different CPU architecture than your server.

  • This image supports: amd64
  • Check yours with uname -m: x86_64 is amd64, aarch64 is arm64. Raspberry Pi and other ARM boards are the usual culprits.

Raise an issue

Found something which isn't working as it should? Here's how to report it.

A single container

Pure-FTP Server runs as one container, the simplest kind of app here. Just the one image to pull and nothing else wired up alongside it.

The app image

An image is the app packed up ready to go, everything Pure-FTP Server needs bundled into one download. This template pulls stilliard/pure-ftpd:hardened, which Docker fetches once (about 59 MB) and then starts your own copy from.

Where the image comes from

Docker pulls its images from registries, public libraries of ready-built apps. Pure-FTP Server's comes from Docker Hub, published by stilliard.

Version tags

The bit after the colon in the image name is the version tag. This one pins hardened, so every redeploy gives you that exact build until you bump it yourself.

Which machines it runs on

Every image is built for particular CPU types. This one ships for amd64, so it runs on regular x86 PCs and servers, though not ARM boards like a Raspberry Pi.

Ports

A port is the door the app answers on. Here the app exposes a port but leaves the host side blank, so Docker picks a free one for you. It opens:

  • 21, published on a random host port
  • 30000, published on a random host port
  • 30001, published on a random host port
  • 30002, published on a random host port
  • 30003, published on a random host port
  • 30004, published on a random host port
  • 30005, published on a random host port
  • 30006, published on a random host port
  • 30007, published on a random host port
  • 30008, published on a random host port
  • 30009, published on a random host port

Volumes

A volume is where Pure-FTP Server keeps its files so they survive an update or a restart. Without one, anything it saves would sit inside the container and vanish the moment it's recreated. This template mounts:

  • /etc/pure-ftpd/passwd from /opt/mediadepot/apps/pureftp on the host
  • /data/tvshows from /media/storage/tvshows on the host
  • /data/movies from /media/storage/movies on the host
  • /data/music from /media/storage/music on the host
  • /data/ebooks from /media/storage/ebooks on the host
  • /data/photos from /media/storage/photos on the host
  • /data/documents from /media/storage/documents on the host
  • /data/downloads from /media/storage/downloads on the host
  • /data/software from /media/storage/software on the host
  • /data/blackhole from /media/temp/blackhole on the host
  • /data/processing from /media/temp/processing on the host

Environment variables

Environment variables are the settings you hand over when you deploy, things like a password or a timezone. Pure-FTP Server takes 7 of them, all with defaults you can leave alone or tweak:

  • FTP_USER_NAME, defaults to depot
  • FTP_USER_PASS, defaults to badpass
  • FTP_USER_HOME, defaults to /data
  • FTP_USER_GID, defaults to 15000
  • FTP_USER_UID, defaults to 15000
  • TZ, defaults to America/Los_Angeles
  • PUBLICHOST, defaults to localhost

Networking

Pure-FTP Server runs on the host network, so it shares your server's networking directly instead of getting a private one of its own. Its ports open straight on the server with no mapping in between.

Container name

Once it's deployed, Portainer names the container pureftp. That's what you'll spot in the containers list and use in commands like docker logs pureftp.

Portainer app templates

Zooming out, this whole page comes from a Portainer app template: a short recipe telling Portainer how to set Pure-FTP Server up. Add the template list to Portainer once, then deploying Pure-FTP Server is a click rather than a wall of config.