
Table of Contents
By Khimananda Oli | Last reviewed: August 2026
A fresh Ubuntu installation does not accept remote connections by default, leaving you locked out of headless servers or cloud instances until you configure remote access correctly. This Ubuntu SSH server setup guide walks you through installing OpenSSH, enforcing key-based authentication, and hardening the daemon against automated attacks before exposing it to any network. Whether you are provisioning a new VPS in Kathmandu or managing a fleet of AWS EC2 instances, these steps establish a secure baseline that prevents credential stuffing and unauthorized root access.
openssh-server package, generate ED25519 keys, disable password authentication in /etc/ssh/sshd_config, configure UFW to allow port 22 only from trusted IPs, and restart the service. Always verify connectivity in a second terminal session before closing your active connection to prevent lockout.How do you install and verify OpenSSH on Ubuntu?
The OpenSSH server package is not included in minimal Ubuntu cloud images or container base layers. You must explicitly install it using APT. Before running any installation commands, ensure your package index is current to avoid pulling outdated binaries with known vulnerabilities.
sudo apt update
sudo apt install -y openssh-server
sudo systemctl enable --now ssh
systemctl status ssh The enable --now flag both starts the service immediately and configures it to launch on boot, which is critical for surviving reboots in production environments. When you check the status, look for "active (running)" in green. If the service fails to start, check journalctl -u ssh -n 50 --no-pager for syntax errors in the configuration file.
After confirming the service is running, perform an initial connectivity test from your local machine using verbose mode. This reveals handshake details and confirms the server responds before you begin modifying configurations:
ssh -v user@your-server-ip If this connects successfully with password authentication, the baseline installation works. Now you can proceed to securing it. Never skip this verification step; debugging connectivity after applying hardening measures without a known-good baseline wastes significant time.
How do you configure SSH key authentication securely?
Password authentication is the single largest attack surface on any SSH daemon. Automated bots scan public IP ranges continuously, attempting credential stuffing against exposed ports. Key-based authentication eliminates this vector entirely when configured correctly. For a complete treatment of hardening beyond keys, see my guide on how to harden SSH with key auth, Fail2Ban, and port hardening.
Generate modern ED25519 keys
RSA keys require 4096 bits for adequate security in 2026, but ED25519 provides equivalent strength at 256 bits with faster operations and smaller signatures. Generate a key pair on your local machine, never on the server itself:
ssh-keygen -t ed25519 -C "[email protected]" -f ~/.ssh/id_ed25519_prod The -C comment field helps identify keys when auditing authorized_keys files across multiple servers. Use descriptive comments including hostname or purpose.
Deploy the public key safely
Use ssh-copy-id to append your public key to the remote server's authorized_keys file. This tool handles permissions and directory creation automatically, preventing common mistakes:
ssh-copy-id -i ~/.ssh/id_ed25519_prod.pub user@your-server-ip Verify key authentication works before disabling passwords. Open a new terminal window and connect specifying the key explicitly:
ssh -i ~/.ssh/id_ed25519_prod user@your-server-ip If this succeeds without prompting for a password, key authentication is functional. Keep your original password-authenticated session open as a fallback until all hardening is complete and verified.
Set correct file permissions
OpenSSH refuses authentication if permissions are too permissive. The server enforces this strictly to prevent unauthorized key injection:
~/.sshdirectory:700(drwx------)~/.ssh/authorized_keys:600(-rw-------)~/.ssh/id_ed25519_prod(private key):600(-rw-------)~/.ssh/id_ed25519_prod.pub(public key):644(-rw-r--r--)
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
chown -R $USER:$USER ~/.ssh What sshd_config settings harden Ubuntu SSH effectively?
Edit /etc/ssh/sshd_config directly or use drop-in files under /etc/ssh/sshd_config.d/ for better maintainability. Drop-ins are processed alphabetically after the main config, making upgrades safer since package updates won't overwrite customizations.
sudo nano /etc/ssh/sshd_config.d/99-hardening.conf Apply these directives for production-grade security:
# Disable password and root authentication
PasswordAuthentication no
PermitRootLogin no
PubkeyAuthentication yes
AuthenticationMethods publickey
# Limit exposure
MaxAuthTries 3
ClientAliveInterval 300
ClientAliveCountMax 2
LoginGraceTime 30
# Restrict ciphers and algorithms
KexAlgorithms [email protected],curve25519-sha256
Ciphers [email protected],[email protected]
MACs [email protected],[email protected]
# Logging
LogLevel VERBOSE Always validate configuration syntax before restarting the daemon. A typo can lock you out completely:
sudo sshd -t This command returns silently on success or prints specific line numbers on error. Only restart after clean validation:
sudo systemctl reload ssh Use reload instead of restart to apply changes without dropping existing sessions. This matters during maintenance windows when other administrators may be connected.
How do you restrict SSH access with UFW firewall rules?
Network-level filtering provides defense-in-depth even when SSH hardening is complete. Uncomplicated Firewall (UFW) wraps nftables with a manageable syntax suitable for most Ubuntu deployments. For deeper firewall architecture decisions, review how to configure UFW on Ubuntu effectively.
Allow SSH from trusted sources only
Never expose port 22 to 0.0.0.0/0 on public servers. Restrict to your office IP, VPN subnet, or bastion host:
sudo ufw allow from 203.0.113.50/32 to any port 22 proto tcp comment 'Admin workstation'
sudo ufw allow from 10.8.0.0/24 to any port 22 proto tcp comment 'WireGuard VPN'
sudo ufw deny 22/tcp The explicit deny rule catches any source not matched by preceding allow rules. Order matters: UFW processes rules top-to-bottom and stops at first match.
Enable and verify UFW
If UFW is inactive, enable it carefully. Ensure you have console access (cloud provider web console or physical access) before enabling remotely:
sudo ufw enable
sudo ufw status numbered Review the numbered output to confirm rules appear in expected order. Test connectivity from an allowed IP and verify rejection from a disallowed IP using a mobile hotspot or different network.
| Configuration Aspect | Insecure Default | Hardened Setting | Risk Mitigated |
|---|---|---|---|
| Authentication | Password enabled | PubkeyAuthentication only | Credential stuffing, brute force |
| Root Login | PermitRootLogin yes | PermitRootLogin no | Direct root compromise |
| Port Exposure | Open to 0.0.0.0/0 | Restricted to trusted IPs | Automated scanning, exploit attempts |
| Cryptographic Algorithms | Legacy CBC, SHA1 | ChaCha20-Poly1305, ETM MACs | Protocol downgrade attacks |
| Session Limits | No timeouts | ClientAliveInterval 300 | Abandoned session hijacking |
What common mistakes break Ubuntu SSH server setup?
Even experienced engineers make predictable errors during SSH configuration. These issues cause lockouts, silent failures, or false security assumptions.
- Closing your only session before testing: Always open a second terminal after config changes. If reload breaks authentication, the original session remains active for rollback.
- Editing sshd_config directly without backups: Run
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak.$(date +%F)before every edit. Version control the directory with etckeeper for audit trails. - Ignoring SELinux/AppArmor contexts: On Ubuntu, AppArmor profiles may block SSH from reading authorized_keys in non-standard locations. Check
aa-statusand review/var/log/syslogfor denials. - Assuming key deployment succeeded:
ssh-copy-idcan silently fail if the remote shell outputs unexpected text (motd banners, profile scripts). Verify manually withcat ~/.ssh/authorized_keyson the server. - Forgetting IPv6: UFW rules applied to IPv4 don't automatically cover IPv6. If your server has global IPv6 addressing, add corresponding v6 rules or disable IPv6 in sshd_config with
AddressFamily inet.
When troubleshooting failed connections, increase client verbosity progressively: ssh -vvv reveals full handshake negotiation, algorithm selection, and authentication method attempts. Match this against server-side logs at LogLevel VERBOSE to pinpoint mismatches.
Finalizing Your Ubuntu SSH Server Setup
A properly hardened Ubuntu SSH server setup requires three elements working together: cryptographic key authentication replacing passwords, restrictive firewall rules limiting exposure, and validated configuration preventing lockouts. Document your final sshd_config in version control alongside infrastructure-as-code definitions so deployments remain reproducible. For teams managing multiple servers, consider automating this entire process with Ansible playbooks as described in my guide to automating server setup with Ansible. If your environment demands compliance-ready infrastructure or you need help auditing existing SSH configurations across a fleet, reach out to discuss your requirements.