A lot of SSH work starts the same way. A client sends a message late in the evening, the ERP is slow, a scheduled job has failed, or the web app is throwing errors after a deployment. You need shell access now, not after hunting through old notes for the right username, key file, port, and transfer command.
That's why strong command-line fluency matters. Commands for SSH aren't just Linux trivia. They're the working tools for checking logs, moving backups, restarting services, opening a temporary tunnel to a database, and getting in and out of production cleanly. When you host Odoo, other ERP platforms, or custom web apps, SSH becomes the control plane for routine maintenance and urgent fixes.
Table of Contents
- Why Mastering SSH Commands is Non-Negotiable
- Foundational SSH Connection Commands
- Managing SSH Keys for Passwordless Authentication
- Mastering Secure File Transfers with SCP SFTP and Rsync
- Advanced Port Forwarding and Tunnelling Techniques
- Efficiently Managing Multiple Servers
- Automating Connections with the SSH Config File
- Essential SSH Security Hardening and Troubleshooting
- Frequently Asked SSH Questions
Why Mastering SSH Commands is Non-Negotiable
SSH is the standard way to log in to a remote machine and execute commands over an encrypted connection. The core usage is simple, usually ssh user@host for a shell session or ssh host command for a one-off task, as documented in the OpenSSH ssh(1) manual. In practical terms, it's the default secure remote access method for administering Linux and Unix systems and it replaced older approaches such as Telnet.
That sounds basic until you're dealing with production. An ERP server usually has scheduled jobs, uploaded documents, a database layer, worker processes, reverse proxy settings, and application logs spread across different paths. A web stack adds service management, deployments, SSL renewal, and file permissions. If you can't move around confidently over SSH, every small task becomes slower and riskier.
There's also a security reason to treat SSH as a core skill, not a side tool. Internet-facing Linux systems still attract constant scanning and attack traffic. Sloppy authentication, weak key handling, and broad login access create problems long before an advanced exploit appears.
Practical rule: If a server matters to the business, your SSH access should be fast, predictable, logged, and tightly scoped.
Good SSH habits reduce mistakes under pressure. You stop improvising in production. You use the right identity file, connect through the right jump host, transfer files with the right tool, and avoid exposing services that should stay private. For ERP and web app hosting, that discipline is the difference between a short maintenance task and a drawn-out incident.
Foundational SSH Connection Commands
The first commands for SSH you should memorise are the ones you'll use every day. Everything else builds on them.
Start with the two command forms that matter
Interactive login gives you a shell on the remote host:
ssh user@server
Non-interactive execution runs a command remotely and returns the output:
ssh server 'df -h'
That second form is underrated. If you only need to check disk space, inspect a service state, or confirm a release file exists, don't open a full shell, type the command, and exit. Run it directly and move on.
A few common variants come up constantly:
ssh -p 2222 user@server
ssh -i ~/.ssh/erp-prod user@server
ssh -p 2222 -i ~/.ssh/erp-prod user@server
Use -p when the SSH daemon listens on a non-default port. Use -i when you need a specific private key instead of whatever your agent offers by default.
Use non-interactive commands when you need an answer fast
For ERP and web app operations, one-off remote commands are ideal for checks like these:
ssh user@server 'systemctl status odoo'
ssh user@server 'tail -n 50 /var/log/nginx/error.log'
ssh user@server 'uptime'
ssh user@server 'free -h'
This style works well in scripts and operational runbooks. It also fits least-privilege workflows. SSH supports both interactive and non-interactive execution, and per-key command restriction can be enforced in authorized_keys with a command="..." prefix, which is useful for tightly scoped access according to SSH command guidance from SSH.com.
That matters when you want a backup key to run one task only, or when an automation job should upload data and trigger a single server-side script, but nothing else.
| Command | Best use | Why it helps |
|---|---|---|
ssh user@server |
Interactive admin work | Full shell access for diagnosis and changes |
ssh server 'command' |
Checks and automation | Faster, scriptable, less room for drift |
ssh -p ... |
Managed hosting with custom SSH port | Connects cleanly without trial and error |
ssh -i ... |
Multiple client or environment keys | Prevents wrong-key confusion |
If your team manages cloud ERP estates across environments, consistent SSH access patterns become part of operational hygiene. That's especially true in mixed hosting setups with app servers, workers, proxies, and managed services, where broader cloud solutions for business applications rely on reliable remote administration rather than ad hoc console access.
Don't treat remote shell access as a fallback. On Linux application hosting, it is often the primary path for accurate operational work.
Managing SSH Keys for Passwordless Authentication
Passwords are familiar, but they're a poor habit on production systems. For SSH, key-based authentication is the baseline to aim for. It's cleaner for operators and harder to abuse than reused or weak passwords.
Mainstream SSH administration guidance recommends preferring key-based authentication, restricting who can log in with AllowUsers or AllowGroups, and restarting the SSH service after changes to reduce brute-force exposure on internet-facing systems, as described in DigitalOcean's SSH essentials guide.
Generate keys once and name them properly
A common mistake is keeping one unnamed default key for everything. That works until you manage multiple clients, environments, or jump hosts.
Use ssh-keygen and name the key for its purpose:
ssh-keygen -t ed25519 -f ~/.ssh/odoo-prod
ssh-keygen -t ed25519 -f ~/.ssh/client-staging
A clear filename prevents accidental reuse. It also makes audits easier. When a contractor leaves or a server is retired, you know which key belongs to what.
Add a passphrase when generating the key. Yes, it adds a step. That step is worth it when a laptop is lost, a home directory is copied, or a workstation session is left accessible.
Copy the public key cleanly
The easiest deployment path is usually:
ssh-copy-id -i ~/.ssh/odoo-prod.pub user@server
That appends the public key to the target account's authorized_keys file without manual copy and paste. It also avoids formatting mistakes that happen when people edit files directly over a terminal session.
If you manage access at scale, think beyond key placement. Identity sprawl becomes an access-control problem very quickly. A broader IAM model matters too, especially across cloud-hosted business systems, and this overview of IAM for cloud security is useful context for deciding who should have persistent shell access and who should not.
Permissions break more key logins than bad commands do
When a key looks correct but login fails, permissions are usually the culprit. The server expects the account's SSH files to be locked down.
Use this pattern on the remote host:
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
If the home directory or ownership is wrong, fix that too. SSH is intentionally strict here.
A few practical habits help:
- Use separate keys: Keep production and non-production identities apart.
- Load keys into an agent: That lets you keep passphrases without retyping them constantly.
- Avoid shared accounts: It's better to know exactly which admin key was used.
- Limit server-side access:
AllowUsersorAllowGroupsnarrows who can authenticate at all.
When you change server authentication settings, reload or restart the SSH service in a planned session, and keep one existing session open while you test. Locking yourself out of a live ERP host because you edited sshd_config too quickly is a very avoidable lesson.
Mastering Secure File Transfers with SCP SFTP and Rsync
File transfer is where many admins waste time. They use one command for every task, even when the workload is completely different. ERP and web app environments need a more deliberate choice because you're dealing with code deploys, document stores, static assets, and database backups.

Use the right tool for the job
Here's the short version:
| Tool | Best for | Strength | Weakness |
|---|---|---|---|
scp |
Quick one-off copy | Simple syntax | Poor fit for repeated sync work |
sftp |
Interactive file management | Better control when browsing remote files | Slower for repetitive bulk workflows |
rsync |
Sync, deploy, backup | Efficient incremental transfer | More options to learn |
scp is the classic fast copy command:
scp backup.sql.gz user@server:/srv/backups/
scp user@server:/var/log/app.log .
It's fine for a single file or a quick pull. It stops being a good habit when you repeat the same large transfers over and over.
sftp gives you an interactive session:
sftp user@server
Inside that session, you can list files, change directories, upload, and download without dropping into a full shell. It's useful when you want a safer, more deliberate workflow than repeated scp commands. If you need a server-side setup reference, this guide to an Ubuntu SFTP server set up securely in 2026 is a practical companion.
What works best in ERP and web app hosting
For recurring transfers, rsync is usually the right answer:
rsync -avz ./addons/ user@server:/srv/odoo/custom-addons/
rsync -avz user@server:/srv/backups/ ./backups/
The reason is simple. For large file transfers common in ERP work, such as database backups, rsync uses a delta-transfer algorithm that only sends file differences, and that can reduce data transfer by over 90% for subsequent syncs, according to the official rsync documentation.
That matters in real operations. Backups change a bit. Uploaded assets change a bit. Application directories change a bit. Sending the whole payload every time is wasteful.
Use
scpwhen the task is small and one-off. Usesftpwhen you need to browse and manage files interactively. Usersyncwhen you expect to do the job again.
A practical way to choose:
- Deploying one config file:
scp - Pulling a few logs while browsing directories:
sftp - Synchronising add-ons, media, or backups:
rsync
One warning on client behaviour. A lot of older tutorials still centre on scp for everything. In mixed Windows and Linux estates, or after server upgrades, the issue is often compatibility rather than the command itself. Current practice has shifted toward stricter defaults, clearer host-key handling, and more caution around older file-copy assumptions, as discussed in Red Hat's overview of SSH usage patterns.
Advanced Port Forwarding and Tunnelling Techniques
Tunnelling is where SSH stops being just a login method and becomes an access tool. If you manage databases, internal web panels, or private APIs, forwarding is often safer than exposing another port to the internet.
A simple mental model helps. SSH can carry more than your shell session. It can carry other TCP traffic inside the encrypted connection.
Here's the basic local forwarding pattern in visual form.

Local forwarding for private admin access
Local forwarding uses -L. It binds a port on your machine and sends that traffic through the SSH server to a target service.
ssh -L 8080:localhost:8069 user@server
That command is useful when an ERP web interface, admin dashboard, or internal app binds only to localhost on the server. You connect over SSH, open your browser locally, and reach the service without publishing it.
This is also the standard answer for database GUI access. If PostgreSQL or another backend should stay private, forward it temporarily rather than opening firewall rules for convenience.
Private services should stay private. SSH forwarding is often the cleanest bridge between access and exposure.
Remote forwarding for callbacks and demos
Remote forwarding uses -R. It does the reverse. A port on the remote side points back to a service on your machine.
ssh -R 9000:localhost:3000 user@server
This is useful for temporary webhooks, demos, or callback-based testing where the remote host needs to reach something running locally. It's not a permanent publishing method. It's a controlled operational tool.
Be careful with it. Remote forwarding can create paths you didn't intend if you don't understand who can reach the forwarded port on the remote side. Keep it short-lived and documented.
A quick video helps if the concept is still abstract.
Dynamic forwarding for SOCKS proxy use
Dynamic forwarding uses -D:
ssh -D 1080 user@server
This turns your SSH client into a SOCKS proxy. Applications configured to use that proxy send traffic through the SSH server. It's handy for secure browsing from a controlled remote point or for testing how a site behaves from the server side of a network path.
In day-to-day ERP support, local forwarding is the most common and the least controversial. Dynamic forwarding is more specialised. Remote forwarding is the one to treat with the most care.
One more advanced point matters in real environments. Modern SSH behaviour and compatibility problems now show up around host keys, algorithms, and transfer workflows, not just simple syntax. In hybrid infrastructure, especially where remote access remains common, SSH reliability becomes part of security hardening, not just operator convenience.
Efficiently Managing Multiple Servers
One host is easy. Real estates aren't one host. You'll often have a bastion, then separate development, staging, workers, databases, and production nodes. If you type every connection from memory, mistakes creep in.

Jump through a bastion without messy proxy commands
The modern way to hop through an intermediate host is -J:
ssh -J bastion-user@bastion app-user@internal-app
That replaces older, messier proxy command patterns for many cases. It's easier to read, easier to support, and much easier to teach to a junior admin.
In hosted ERP environments, bastions are common because private application servers shouldn't be directly reachable from everywhere. If your infrastructure model already isolates workloads, a dedicated Odoo hosting environment often follows this pattern with tightly controlled ingress and an operational jump path rather than broad direct shell exposure.
A sensible naming convention helps too. Use aliases like bastion-prod, odoo-stage, and worker-prod-01. Human-readable names reduce accidental connections to the wrong place.
Multiplexing saves time when you live in the shell
SSH multiplexing lets one established connection act as a master session for later connections to the same host. The result is simple. New shells, remote commands, and some file operations start faster because they reuse the existing TCP connection.
This is especially useful when you're doing a run of production tasks:
- Check service state
- Tail logs
- Copy a config
- Run a restart command
- Verify the result
Without multiplexing, each step may trigger a fresh connection setup. With it, the flow feels much smoother.
If you connect to the same host repeatedly within a short maintenance window, multiplexing removes a lot of friction without reducing control.
There is a trade-off. Reusing a connection is efficient, but it also means your local client state matters more. If your workstation is poorly secured, convenience can work against you. On admin machines, that's a reason to keep screen locking, disk encryption, and SSH agent discipline in good shape.
Automating Connections with the SSH Config File
Typing long SSH commands is a waste of attention. The actual cost isn't the keystrokes. It's the mistakes when you're tired and moving quickly.
Turn long commands into short names
This is the before state:
ssh -i ~/.ssh/odoo-prod -p 2222 appuser@server
This is the after state:
ssh odoo-prod
That change comes from ~/.ssh/config. Once you define the host alias and its settings, SSH remembers the details.
A simple entry looks like this:
Host odoo-prod
HostName server
User appuser
Port 2222
IdentityFile ~/.ssh/odoo-prod
Now the connection command is short, memorable, and far less likely to be typed incorrectly during an incident.
A practical config pattern
The config file gets more useful as your estate grows. You can define staging, production, bastion hosts, and inherited defaults.
Host bastion-prod
HostName bastion
User ops
IdentityFile ~/.ssh/bastion-prod
Host odoo-stage
HostName stage-app
User appuser
IdentityFile ~/.ssh/odoo-stage
ProxyJump bastion-prod
Host odoo-prod
HostName prod-app
User appuser
IdentityFile ~/.ssh/odoo-prod
ProxyJump bastion-prod
You can also place forwarding directives there, which is useful when you repeatedly need a local tunnel for a private admin tool.
A few good habits make the file maintainable:
- Group by environment: Keep staging and production entries visibly separate.
- Use descriptive aliases: Short names are good. Ambiguous names are not.
- Comment unusual settings: If a host needs older compatibility flags or a special jump path, leave a note.
- Keep stale entries out: Retired hosts create confusion.
The SSH config file pays for itself quickly. It standardises access, reduces command recall under pressure, and makes handovers easier when another admin needs to work the same environment.
Essential SSH Security Hardening and Troubleshooting
Default SSH access is rarely the access model you want on a production system. Harden it deliberately.

Hardening steps worth doing first
In Q1 2026, ASEC's honeypot analysis of Linux SSH servers found that P2PInfect accounted for 70.3% of all attack sources, with Mirai and XMRig also among the main threats, according to ASEC's Linux SSH honeypot analysis. That's enough reason to treat SSH hardening as routine maintenance, not a special project.
Start with the basics:
- Disable password authentication: If you've deployed keys properly, remove the weaker path.
- Disable direct root login: Admins should authenticate as themselves, then escalate when needed.
- Restrict login scope: Limit access to named users or groups.
- Review service exposure: If a server doesn't need broad remote administration, don't leave unnecessary paths open.
- Use rate-limiting or banning controls: Brute-force noise is constant on exposed systems.
After changes, test from a second session before closing the first. That one habit prevents a lot of self-inflicted outages.
Troubleshooting the errors people hit most
Permission denied (publickey) usually means one of four things. The wrong key is being offered, the remote account is wrong, file permissions are too open, or the public key was never correctly installed.
Connection refused usually points to the service not listening, the port being wrong, or access controls blocking the path. If you suspect network delay or route issues rather than SSH itself, this guide on identifying network bottlenecks is a useful troubleshooting complement.
WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! should never be ignored casually. Sometimes a server was rebuilt and the host key legitimately changed. Sometimes you're connecting to the wrong machine. Sometimes something more serious is happening. Verify first, then remove the stale known_hosts entry and reconnect only when you're confident.
Changed host keys are a verification task, not an annoyance to click past.
Frequently Asked SSH Questions
Why do I get Permission denied (publickey) when my key looks correct
Most of the time, the key isn't the only issue. Check the remote username, confirm you're using the intended identity file, and inspect ~/.ssh plus authorized_keys permissions on the server. SSH rejects keys if the file permissions are too loose.
What's the difference between ssh-agent and ssh-add
ssh-agent is the background process that holds accessible keys in memory for your session. ssh-add loads a private key into that agent. If you want passphrase-protected keys without typing the passphrase for every connection, you usually need both.
Should I use agent forwarding
Only when you understand why you need it. Agent forwarding can be convenient when hopping through trusted systems, but it also extends trust to the remote environment. On shared or less trusted hosts, it's often safer to avoid it and use a better jump-host design instead.
What does the host identification warning actually mean
It means the host key presented by the server doesn't match what your client previously stored. Treat it as a verification event. Don't delete the warning blindly.
Is SSH enough on its own for access security
SSH keys are strong, but access control shouldn't stop there. Broader identity protection still matters, and this explanation of multi-factor authentication's role is useful when you're designing access around admin workstations, cloud consoles, and related systems outside the SSH session itself.
If your team needs help securing, hosting, or operating Odoo and related business systems, ERP Artists can support the full path from implementation and custom development to cloud hosting, integrations, and ongoing production support.