Uploading a whole directory to a file-transfer endpoint usually means the destination job expects one deployable bundle, not hundreds of separate files. For release staging, partner handoff, and backup export workflows, packaging the tree first keeps the transfer predictable and gives the receiver one artifact to verify.

With cURL, a directory upload is really a file upload. cURL does not walk a local directory recursively and send each entry for FTP or SFTP, so the practical pattern is to archive the directory, upload that archive with --upload-file, and unpack it later only if the remote workflow needs the original tree again. If the destination truly requires each file to be transferred separately, use a different tool or a script loop outside this page.

In FTP URLs, a single slash after the hostname is relative to the login directory, while a double slash targets the server root. In SFTP URLs, paths are absolute by default, and /~/ means the remote login account's home directory. Check protocol support first because some curl builds include ftp but omit sftp, and use .netrc or SSH keys instead of putting credentials directly on the command line.

Steps to upload directories to FTP or SFTP with cURL:

  1. Confirm that the local cURL build supports the protocol you need before preparing the transfer.
    $ curl --version
    curl 8.15.0 (x86_64-pc-linux-gnu) libcurl/8.15.0 OpenSSL/3.0.13 zlib/1.3.1 libssh2/1.11.1
    Protocols: dict file ftp ftps gopher gophers http https imap imaps ipfs ipns mqtt pop3 pop3s rtsp scp sftp smb smbs smtp smtps telnet tftp
    Features: alt-svc AsynchDNS HSTS HTTP2 IPv6 Largefile libz SSL threadsafe UnixSockets

    The Protocols line must include ftp for FTP uploads and sftp for SFTP uploads. If sftp is missing, the SFTP steps on this page fail until you use a curl build with SSH support.

  2. Pack the directory into one archive before sending it.
    $ cd /srv/releases
    $ tar -czf partner-portal-2026-04.tar.gz partner-portal
    $ ls -lh partner-portal-2026-04.tar.gz
    -rw-r--r--  1 user  user   18M Apr 22 09:14 partner-portal-2026-04.tar.gz

    cURL uploads files, not directory trees, so the archive becomes the single object that is transferred and verified.

  3. Upload the archive to the target FTP path and let cURL create missing remote directories when needed.
    $ curl --ftp-create-dirs \
      --netrc \
      --upload-file partner-portal-2026-04.tar.gz \
      ftp://ftp-upload.example.net/incoming/releases/2026/04/partner-portal-2026-04.tar.gz
      % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                     Dload  Upload   Total   Spent    Left  Speed
    100 18.2M    0     0  100 18.2M      0  12.0M  0:00:01  0:00:01 --:--:-- 12.1M

    Use ftp://host//incoming/releases/... when the destination path must start at the server root instead of the login directory.

    Plain FTP does not encrypt credentials or file contents. Use SFTP or FTPS when the network path is not already trusted.

  4. List the remote FTP directory and confirm that the uploaded archive name is present before any extract or publish job uses it.
    $ curl --silent --show-error --list-only --netrc ftp://ftp-upload.example.net/incoming/releases/2026/04/
    partner-portal-2026-04.tar.gz

    The uploaded archive should appear in the listing before any downstream unpack, checksum, or deploy step runs.

  5. Upload the same archive to SFTP with SSH key authentication and let cURL create the missing directory tree.
    $ curl --ftp-create-dirs \
      --user releasebot: \
      --key ~/.ssh/id_ed25519 \
      --upload-file partner-portal-2026-04.tar.gz \
      sftp://sftp-upload.example.net/~/incoming/releases/2026/04/partner-portal-2026-04.tar.gz
      % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                     Dload  Upload   Total   Spent    Left  Speed
    100 18.2M    0     0  100 18.2M      0  11.3M  0:00:01  0:00:01 --:--:-- 11.4M

    cURL usually derives the public key from --key automatically. Add --pubkey ~/.ssh/id_ed25519.pub only when the SSH backend cannot derive it.

  6. List the destination SFTP directory and confirm the archive landed where the remote workflow expects it.
    $ curl --silent --show-error --list-only \
      --user releasebot: \
      --key ~/.ssh/id_ed25519 \
      sftp://sftp-upload.example.net/~/incoming/releases/2026/04/
    partner-portal-2026-04.tar.gz

    If the file lands in the wrong place, check the path style first. FTP defaults to the login directory, while SFTP paths are absolute unless you deliberately use /~/.