SSH is surprisingly slow sometimes, and it’s usually not the network.
Let’s see SSH in action. Imagine you’re pulling a large file from a remote server over a relatively slow, high-latency connection. Without optimization, this will feel like pulling teeth.
# On your local machine
scp -P 2222 large_file.tar.gz user@remote_host:/path/to/destination/
# On the remote server (where the file is)
scp -P 2222 large_file.tar.gz user@remote_host:/path/to/destination/
The default SSH configuration is often a compromise, prioritizing compatibility over speed. This means it might be using encryption algorithms that are computationally expensive, or it might not be compressing data at all, sending every byte raw over the wire.
The core problem SSH optimization solves is the overhead introduced by encryption and the inefficiency of sending uncompressed data. Every byte you send or receive is processed by an encryption/decryption algorithm. If the data is repetitive (like text files or configuration files), sending it uncompressed is like driving an empty truck when you could be carrying a full load.
Here’s how we can tune it.
Compression
SSH has a built-in compression feature. It’s not as aggressive as dedicated compression tools like gzip or zstd, but it’s often enough to make a noticeable difference, especially on high-latency, low-bandwidth links.
Diagnosis:
To see if compression is active, check your ssh_config (local) or sshd_config (remote) files. Look for the Compression directive. If it’s not explicitly set, it defaults to no.
Fix:
Add or modify the following line in your ~/.ssh/config file (for client-side optimization) or /etc/ssh/sshd_config (for server-side, requires restart):
# In ~/.ssh/config
Host *
Compression yes
CompressionLevel 6
This tells the SSH client to attempt compression for all connections. CompressionLevel can be set between 1 (fastest, least compression) and 9 (slowest, most compression). Level 6 is a good balance.
Why it works:
When Compression yes is enabled, the SSH client compresses data before encrypting it. On the receiving end, SSH decrypts the data and then decompresses it. This reduces the total amount of data that needs to be transmitted, saving bandwidth and often speeding up transfers over slow links. The CompressionLevel controls how much effort is put into compression; higher levels mean smaller data but more CPU usage.
Ciphers
The choice of encryption cipher has a significant impact on performance. Some ciphers are very strong but computationally intensive, consuming more CPU resources on both the client and server. Others are faster but might be considered less secure (though modern SSH ciphers are generally very secure).
Diagnosis:
You can see the negotiated cipher during an SSH session by running ssh -v or ssh -vvv. Look for lines like:
debug1: cipher_inbuf_new: creating cipher_inbuf (16384 bytes)
debug1: kex: algorithm: curve25519-sha256@libssh.org
debug1: kex: hostkey algorithm: ssh-ed25519
debug1: kex: server->client cipher: aes128-gcm@openssh.com MAC: <none> compression: none
debug1: kex: client->server cipher: aes128-gcm@openssh.com MAC: <none> compression: none
The cipher: aes128-gcm@openssh.com part is what you’re interested in.
Fix:
You can prioritize faster ciphers by specifying them in your ~/.ssh/config file. For example, to prioritize aes128-gcm@openssh.com and chacha20-poly1305@openssh.com (both known for good performance):
# In ~/.ssh/config
Host *
Ciphers chacha20-poly1305@openssh.com,aes128-gcm@openssh.com,aes256-gcm@openssh.com,aes128-ctr,aes192-ctr,aes256-ctr
MACs hmac-sha2-512-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-512,hmac-sha1,umac-128@openssh.com
The order matters. SSH will try the first cipher in the list.
Why it works:
Modern CPUs often have hardware acceleration for AES-NI instructions, making AES-based ciphers very fast. ChaCha20-Poly1305 is another fast cipher that doesn’t rely on AES-NI and can perform very well on systems without it. By listing these faster ciphers first, you’re instructing the SSH client to negotiate the quickest secure encryption possible, reducing the CPU burden on both ends. Note that GCM modes are generally faster and more secure than CTR modes.
Key Exchange Algorithms and Host Keys
While less impactful than ciphers for ongoing data transfer, the initial key exchange and host key verification can also contribute to connection setup time. Modern algorithms like curve25519-sha256@libssh.org for key exchange and ssh-ed25519 for host keys are generally very fast and secure.
Diagnosis:
This is also visible in the -v output during connection establishment. Look for kex: and hostkey algorithm: lines.
Fix:
These are typically configured on the server-side (sshd_config) and client-side (ssh_config).
# In ~/.ssh/config
Host *
KexAlgorithms curve25519-sha256@libssh.org,diffie-hellman-group-exchange-sha256
HostKeyAlgorithms ssh-ed25519,ecdsa-sha2-nistp521,ecdsa-sha2-nistp384,ecdsa-sha2-nistp256,rsa-sha2-512,rsa-sha2-256
Why it works: Faster, modern algorithms for key exchange and host key verification reduce the computational cost of establishing the secure tunnel, leading to quicker connection setup times.
TCP Tuning
While not strictly SSH, the underlying TCP connection can also be a bottleneck. High latency connections can suffer from TCP’s slow start.
Diagnosis:
Use ping to measure round-trip time (RTT) and iperf3 to measure raw bandwidth. If RTT is high (e.g., >100ms) and iperf3 shows significantly less throughput than your advertised link speed, TCP tuning might help.
Fix:
On Linux, you can experiment with sysctl parameters. For high-latency links, increasing net.ipv4.tcp_rmem and net.ipv4.tcp_wmem (receiver and sender memory buffers) can help. For example:
# Example values, adjust based on your RTT and bandwidth
sudo sysctl -w net.core.rmem_max=16777216
sudo sysctl -w net.core.wmem_max=16777216
sudo sysctl -w net.ipv4.tcp_rmem='4096 87380 16777216'
sudo sysctl -w net.ipv4.tcp_wmem='4096 65536 16777216'
sudo sysctl -w net.ipv4.tcp_congestion_control=bbr # If available and applicable
Make these persistent by adding them to /etc/sysctl.conf.
Why it works: Larger TCP buffers allow more data to be "in flight" on the network, which is crucial for saturating high-latency links. TCP congestion control algorithms like BBR (Bottleneck Bandwidth and Round-trip propagation time) are designed to better handle packet loss and high latency by estimating the available bandwidth and RTT, leading to more efficient data transfer.
The Next Hurdle
After optimizing your SSH connection, you might find that your terminal becomes sluggish when displaying large amounts of output, leading you to investigate terminal multiplexers like tmux or screen for scrollback buffering and session persistence.