How to Copy Files from One Linux Server to Another

Deepak Prasad
Tested on RHEL 10.2 (Coughlan)
Package openssh-clients 9.9p1-25.el10_2
rsync 3.4.4-1.el10_2
nmap-ncat 7.92-5.el10
curl 8.12.1
wget 1.24.5
Applies to RHEL, Rocky Linux, AlmaLinux, Fedora, Debian, Ubuntu, and other Linux distributions with OpenSSH client tools
Privilege SSH access to the remote host as a user that can read the source path and write the destination path; ncat and HTTP examples also need a reachable TCP port on the receiving host
Scope One-time and repeatable file copy with scp, rsync, SFTP batch mode, tar over ssh, ssh with cat, ncat, and HTTP download or upload with curl or wget. Does not cover NFS exports or SSHFS mounts.
Related guides scp command
rsync command
SSH copy folder local to remote
OpenSSH authentication methods
curl command

You usually copy files between Linux servers when you deploy code, move logs, or pull backups. Most methods below use SSH for encryption. Pick the tool by job shape: scp for a quick file, rsync for directories you will sync again — see scp vs rsync comparison when that choice is not obvious. SFTP batch mode fits scripted transfers, and tar over ssh helps when you have many small files. ncat and plain HTTP are valid fallbacks when SSH is not the right fit, but they are not encrypted unless you wrap them in TLS yourself.


Quick reference: copy files between Linux servers

Method Example command Jump to
scp (push file) scp /path/file user@remote-host:/tmp/ scp push
scp (pull file) scp user@remote-host:/path/file /local/dir/ scp pull
scp (recursive) scp -r /path/dir user@remote-host:/tmp/ scp directories
scp (server to server) scp user1@server1:/path/file user2@server2:/path/ server-to-server scp
rsync (push) rsync -av /path/dir/ user@remote-host:/tmp/dir/ rsync
rsync (pull) rsync -av user@remote-host:/path/dir/ /local/dir/ rsync
SFTP batch sftp -b batch.txt user@remote-host SFTP batch
tar over ssh tar -C /src -czf - dir | ssh user@remote-host 'tar -xzf - -C /dest' tar over SSH
ssh + cat ssh user@remote-host 'cat > /remote/file' < /local/file ssh cat
ncat ncat --send-only remote-host 19000 < /local/file (receiver listens with ncat -l) ncat
HTTP download curl -fsS http://remote-host:8080/file -o /local/file HTTP download
HTTP upload curl -X PUT --data-binary @/local/file http://remote-host:8080/upload HTTP upload

Choose the right copy method

Goal Prefer
Copy one file right now scp
Copy from one remote server to another scp user1@server1:/path user2@server2:/path
Sync a directory repeatedly rsync -av
Script put / get without a shell SFTP -b batch file
Thousands of small files tar streamed over ssh
One file and scp is not available, but SSH works ssh user@host 'cat > /dest' < /local/file
Temporary transfer on a trusted LAN with no SSH ncat listener plus sender
File is already on a web server or REST API curl or wget over HTTP or HTTPS
Browse remote paths interactively Interactive sftp (see SFTP chroot guide for restricted uploads)

On OpenSSH 9 and later, scp often uses the SFTP protocol internally. The scp command remains convenient for one-liners even though rsync and SFTP are better fits for sync and automation.


Method 1: Copy a file with scp

scp is the fastest one-liner when you need to push or pull a single file. Set REMOTE once if you run several examples against the same host:

bash
REMOTE=root@192.168.56.156

Push a local file to the remote server:

bash
scp /root/file-copy-lab/src/hello.txt "$REMOTE:/tmp/hello-scp.txt"

scp returns exit status 0 on success. Depending on how it is run, you may also see a progress meter, so verify the destination when demonstrating the result:

bash
ssh "$REMOTE" cat /tmp/hello-scp.txt

Sample output:

output
alpha

Pull the file back to the local machine:

bash
scp "$REMOTE:/tmp/hello-scp.txt" /tmp/hello-pull.txt

Read the local copy:

bash
cat /tmp/hello-pull.txt

Sample output:

output
alpha

Include the trailing colon on user@host: even when the path is the remote home directory. Without it, scp treats the target as a local path.


Method 2: Copy a directory with scp

Recursive scp copies an entire directory tree in one command.

bash
scp -r /root/file-copy-lab/src "$REMOTE:/tmp/copy-src-scp"

List the files that landed on the remote host:

bash
ssh "$REMOTE" 'find /tmp/copy-src-scp -type f | sort'

Sample output:

output
/tmp/copy-src-scp/hello.txt
/tmp/copy-src-scp/sub/nested.txt

scp -r re-copies everything on each run. For directories you update often, rsync is more efficient.


Method 3: Copy between two remote servers with scp

When both servers are reachable from the machine where you run the command, scp can copy directly between them. The local host pulls from the first remote and pushes to the second; the file does not need to land on your laptop unless you want it there.

bash
scp user1@server1:/tmp/file.txt user2@server2:/tmp/

In the lab, copy from vm2 to the local host loopback to show the two-remote syntax on one client:

bash
scp "$REMOTE:/tmp/hello-scp.txt" root@127.0.0.1:/tmp/server-to-server-scp.txt

Confirm the file on the destination host:

bash
cat /tmp/server-to-server-scp.txt

Sample output:

output
alpha

OpenSSH can also copy origin to destination without streaming through your workstation when you pass -R, but that mode requires the first remote host to authenticate to the second. The default two-remote form above is simpler when your SSH keys and known_hosts entries are already set up on the client machine.


Method 4: Copy files with rsync

rsync compares source and destination and sends only differences. Trailing slashes matter: src/ means copy the contents of src into the destination directory.

Push a directory to the remote server:

bash
rsync -av /root/file-copy-lab/src/ "$REMOTE:/tmp/copy-src-rsync/"

Sample output:

output
sending incremental file list
created directory /tmp/copy-src-rsync
./
hello.txt
sub/
sub/nested.txt

sent 244 bytes  received 111 bytes  64.55 bytes/sec
total size is 13  speedup is 0.04

Pull the same tree back to the local host:

bash
rsync -av "$REMOTE:/tmp/copy-src-rsync/" /tmp/copy-pull-rsync/

Sample output:

output
receiving incremental file list
created directory /tmp/copy-pull-rsync
./
hello.txt
sub/
sub/nested.txt

sent 73 bytes  received 236 bytes  88.29 bytes/sec
total size is 13  speedup is 0.04

The -a flag preserves permissions and timestamps where possible; -v lists each path as it is transferred. See the rsync command cheat sheet for --delete, --dry-run, and bandwidth limits.


Method 5: Copy files with SFTP batch mode

Interactive sftp suits manual browsing. For scripts, put commands in a batch file and pass -b.

Create a batch file that uploads one file:

bash
printf 'put /root/file-copy-lab/src/hello.txt /tmp/hello-sftp.txt\n' > /tmp/sftp-batch.txt

Run the batch non-interactively:

bash
sftp -b /tmp/sftp-batch.txt "$REMOTE"

Sample output:

output
sftp> put /root/file-copy-lab/src/hello.txt /tmp/hello-sftp.txt

Verify the upload:

bash
ssh "$REMOTE" cat /tmp/hello-sftp.txt

Sample output:

output
alpha

Add get remote-path local-path lines to download files, or put -r for a directory upload. For passwordless automation, use SSH keys as in these examples.


Method 6: Copy many small files with tar over ssh

scp and rsync spend time per file. When you need to move a directory with thousands of small files, stream a tarball over SSH instead of opening many separate transfers.

Create the destination directory on the remote host first:

bash
ssh "$REMOTE" 'rm -rf /tmp/copy-tar && mkdir -p /tmp/copy-tar'

Stream a compressed archive from local to remote:

bash
tar -C /root/file-copy-lab -czf - src | ssh "$REMOTE" 'tar -xzf - -C /tmp/copy-tar'

Confirm the extracted tree:

bash
ssh "$REMOTE" 'find /tmp/copy-tar/src -type f | sort'

Sample output:

output
/tmp/copy-tar/src/hello.txt
/tmp/copy-tar/src/sub/nested.txt

To copy from remote to local, reverse the pipe: ssh user@remote-host 'tar -C /path -czf - dir' | tar -xzf - -C /local/dest.


Method 7: Copy a file with ssh and cat

When you already have SSH access and only need to push one file, redirect stdin on the remote side with cat. The transfer rides inside the SSH session, so it is encrypted like scp.

Push a local file to the remote host:

bash
ssh "$REMOTE" 'cat > /tmp/ssh-cat-recv.txt' < /root/file-copy-lab/ssh-cat.txt

Confirm the file on the remote host:

bash
ssh "$REMOTE" cat /tmp/ssh-cat-recv.txt

Sample output:

output
ssh-cat-payload

This pattern is handy in scripts when scp is missing but ssh is installed. It does not preserve timestamps or resume partial transfers the way rsync does.


Method 8: Copy a file with ncat

ncat (from the nmap-ncat package on RHEL) can move a file over a plain TCP socket. One host listens, the other connects and streams bytes. There is no encryption and no authentication, so use it only on trusted networks or for a quick rescue copy.

Open the destination port on the receiving host if a host firewall is active:

bash
ssh "$REMOTE" 'firewall-cmd --add-port=19000/tcp'

Start a listener on the receiver that writes incoming data to a file:

bash
ssh "$REMOTE" 'rm -f /tmp/nc-recv.bin; ncat -l 19000 > /tmp/nc-recv.bin' &

Wait until the listener has bound port 19000 before sending:

bash
until ssh "$REMOTE" "ss -ltn | grep -q ':19000 '"; do sleep 0.2; done

From the sender, connect and stream the local file with --send-only so ncat closes after the transfer:

bash
ncat --send-only 192.168.56.156 19000 < /root/file-copy-lab/payload.txt

Wait for the background listener to finish writing the file:

bash
wait

Verify the received file:

bash
ssh "$REMOTE" cat /tmp/nc-recv.bin

Sample output:

output
nc-transfer-payload

Remove the temporary firewall rule when you are done:

bash
ssh "$REMOTE" 'firewall-cmd --remove-port=19000/tcp'

On Debian and Ubuntu, install the ncat package if you want to use these commands. netcat-openbsd and other nc implementations have similar purposes but their options can differ. Prefer SSH-backed tools for routine server copies.


Method 9: Download a file over HTTP

When the source file is already served over HTTP, download it with curl or wget. This is common for vendor installers, public artifacts, or an internal file share behind a web server.

On the host that serves the file, publish it with a simple static server:

bash
ssh "$REMOTE" 'mkdir -p /tmp/http-serve; printf "http-download-payload\n" > /tmp/http-serve/data.txt'

Open the port if a firewall blocks inbound HTTP:

bash
ssh "$REMOTE" 'firewall-cmd --add-port=18888/tcp'

Start Python's built-in HTTP server on port 18888 and record its PID:

bash
ssh "$REMOTE" 'cd /tmp/http-serve; nohup python3 -m http.server 18888 </dev/null >/tmp/http-serve.log 2>&1 & echo $! >/tmp/http-serve.pid'

Wait until the server is listening before downloading:

bash
until ssh "$REMOTE" "ss -ltn | grep -q ':18888 '"; do sleep 0.2; done

Download the file from the other host with curl:

bash
curl -fsS http://192.168.56.156:18888/data.txt -o /tmp/http-download.txt

Read the downloaded copy:

bash
cat /tmp/http-download.txt

Sample output:

output
http-download-payload

wget works the same way when you only need a download:

bash
wget -qO /tmp/http-download-wget.txt http://192.168.56.156:18888/data.txt

Stop the test server and remove the temporary firewall rule:

bash
ssh "$REMOTE" 'kill "$(cat /tmp/http-serve.pid)" 2>/dev/null || true; rm -f /tmp/http-serve.pid; firewall-cmd --remove-port=18888/tcp'

For production URLs, use https:// and verify certificates. See the curl command cheat sheet for headers, auth, and retry flags.


Method 10: Upload a file over HTTP with curl

HTTP upload is less universal than download because the server must expose an endpoint that accepts PUT or POST. REST APIs, object storage gateways, and custom upload handlers fit this pattern.

The receiver below is a minimal Python handler that saves a PUT body to disk. Copy it to the remote host:

bash
cat > /tmp/put-server.py <<'PY'
from http.server import BaseHTTPRequestHandler, HTTPServer
class Handler(BaseHTTPRequestHandler):
    def do_PUT(self):
        n = int(self.headers.get("Content-Length", 0))
        with open("/tmp/http-put-recv.bin", "wb") as f:
            f.write(self.rfile.read(n))
        self.send_response(201)
        self.end_headers()
    def log_message(self, fmt, *args):
        pass
HTTPServer(("0.0.0.0", 18889), Handler).serve_forever()
PY

Copy the script to the remote host:

bash
scp /tmp/put-server.py "$REMOTE:/tmp/put-server.py"

Open the upload port and start the handler in the background:

bash
ssh "$REMOTE" 'firewall-cmd --add-port=18889/tcp; nohup python3 /tmp/put-server.py </dev/null >/tmp/put-server.log 2>&1 & echo $! >/tmp/put-server.pid'

Wait until the upload handler is listening:

bash
until ssh "$REMOTE" "ss -ltn | grep -q ':18889 '"; do sleep 0.2; done

Upload a local file with curl:

bash
curl -fsS -X PUT --data-binary @/root/file-copy-lab/put.txt http://192.168.56.156:18889/upload

Confirm the uploaded bytes on the receiver:

bash
ssh "$REMOTE" cat /tmp/http-put-recv.bin

Sample output:

output
http-put-payload

Many real APIs expect POST with multipart form data instead of raw PUT. Swap -X PUT for -F file=@/path/to/file when the API docs require it.

Stop the upload handler and remove the temporary firewall rule:

bash
ssh "$REMOTE" 'kill "$(cat /tmp/put-server.pid)" 2>/dev/null || true; rm -f /tmp/put-server.pid; firewall-cmd --remove-port=18889/tcp'

Troubleshooting

Symptom Likely cause Fix
Permission denied (publickey) SSH key or account access Confirm ssh user@remote-host works; see OpenSSH authentication
Host key verification failed Remote host key not in known_hosts SSH once interactively or use ssh-keyscan before scripting remote-to-remote copies
scp: remote mkdir failed Parent directory missing on remote ssh user@remote-host mkdir -p /dest/path before rsync or scp
rsync: command not found on remote rsync not installed on one side Install rsync on both hosts or use scp / tar over ssh
Transfer hangs Firewall, routing, or DNS Check route and SSH port 22; see check internet connection
scp copied to local cwd unexpectedly Missing : on user@host:path Always write user@host:/path or user@host:. for home directory
ncat: No route to host or connection refused Firewall or nothing listening Open the TCP port on the receiver; confirm ss -tlnp shows the listener
curl: (7) Failed to connect on HTTP copy Server not running or port blocked Start the HTTP service; open the port in firewalld or the cloud security group
HTTP upload returns 405 Method Not Allowed Endpoint does not accept PUT Match the API method (POST, multipart form, or signed URL) documented by the service

References


Summary

Copying files between Linux servers usually comes down to SSH-backed tools. Use scp when you want a single push or pull command, rsync -av when you will sync the same tree again, SFTP batch mode when a script needs explicit put and get lines, and tar piped through ssh when many small files make per-file tools slow. When SSH file tools are not the right fit, ssh with cat still encrypts a single-file push, ncat can move bytes over an open port on a trusted LAN, and curl or wget handle HTTP downloads or REST-style uploads when a web server or API is already in place.

Define REMOTE=user@host once, confirm plain ssh works before copying, and match trailing slashes on rsync paths. On modern OpenSSH, scp is still fine for one-offs even though rsync is the better default for repeat directory syncs. Treat ncat and plain HTTP as exceptions: open only the ports you need, and prefer HTTPS or SSH for sensitive data. Persistent shared folders belong to NFS or similar mount workflows, not the one-time copy commands covered here.

For deeper flag coverage, open the scp and rsync cheat sheets after you have a working transfer path.


Frequently Asked Questions

1. What is the best command to copy files between two Linux servers?

Use rsync -av for directory sync and repeat transfers because it sends only changes. Use scp for a quick one-off file copy. Use SFTP batch mode when you need scripted put and get operations without an interactive shell.

2. Does scp still work on modern OpenSSH?

Yes. On OpenSSH 9 and later the scp command usually transfers over SFTP internally, but the scp syntax still works for simple push and pull copies.

3. How do I copy a folder from one server to another?

Use scp -r for a one-time recursive copy or rsync -av source/ user@remote-host:/dest/ to preserve permissions and sync only changes on later runs.

4. How do I copy files without installing rsync on both sides?

Use scp or pipe tar over ssh: tar -C /source -czf - dir | ssh user@remote-host "tar -xzf - -C /dest". Both sides need ssh and tar, which are standard on Linux servers.

5. Can I copy directly from one remote server to another?

Yes. Run scp user1@server1:/path user2@server2:/path from a host that can SSH to both servers. The client pulls from the first remote and pushes to the second. OpenSSH also supports scp -R for direct origin-to-destination copying, but that requires the first remote to authenticate to the second.

6. Can I transfer files with netcat or HTTP instead of SSH?

Yes. ncat can stream a file when one host listens and the other connects, but the transfer is not encrypted and you must open a firewall port. HTTP download with curl or wget works when a web server already exposes the file. HTTP upload needs a server endpoint that accepts PUT or POST, such as a REST API or a small upload handler.

7. When should I avoid ncat or plain HTTP for file copy?

Avoid ncat and unencrypted HTTP on untrusted networks or for sensitive data. Prefer scp, rsync, or SFTP over SSH when both hosts already have SSH access. Use HTTP when the file is already published on a web server or when an application API is the intended upload path.
Omer Cakmak

Linux Administrator

Highly skilled at managing Debian, Ubuntu, CentOS, Oracle Linux, and Red Hat servers. Proficient in bash scripting, Ansible, and AWX central server management, he handles server operations on OpenStack, KVM, Proxmox, and VMware.

  • Debian
  • Ubuntu
  • Linux
  • Red Hat Enterprise Linux
  • Shell Script
  • System Administration