If you run n8n self-hosted and need to deploy code, restart services, pull logs, or sync files on a remote Linux server, the SSH node lets you do all of it directly inside your workflow — no external CI pipeline required.
This guide covers every operation, credential setup, common patterns, gotchas, and a free workflow JSON you can import today.
What the SSH Node Does
The n8n SSH node connects to a remote server over SSH and lets you:
- Execute Command — run any shell command or script and capture stdout/stderr
- Download File — pull a file from the remote server into the workflow as binary data
- Upload File — push binary data from the workflow to a path on the remote server
All three operations use the same SSH credential (username + password or private key).
Credential Setup
Option A — Password Authentication
- Go to Credentials → New → SSH.
- Fill in:
- Host: your server IP or hostname
-
Port:
22(default) -
Username: your SSH user (e.g.,
ubuntu,ec2-user) - Authentication Type: Password
- Password: your SSH password
Password auth works but is less secure. Use key-based auth for production.
Option B — Private Key Authentication (Recommended)
- Generate a key pair if you don't have one:
ssh-keygen -t ed25519 -C "n8n-automation"
- Append
~/.ssh/id_ed25519.pubto~/.ssh/authorized_keyson the remote server. - In n8n credentials:
- Authentication Type: Private Key
-
Private Key: paste the full contents of
~/.ssh/id_ed25519(including-----BEGIN...-----ENDlines) - Passphrase: if your key has one
Gotcha: n8n expects the private key in OpenSSH format. If you generated an older RSA key with PuTTYgen (.ppk format), convert it first:
puttygen key.ppk -O private-openssh -o id_rsa
Operations
Execute Command
Runs a shell command on the remote server and returns stdout, stderr, and exit code.
Node settings:
- Operation: Execute Command
-
Command: any shell command string (e.g.,
systemctl restart nginx)
Output fields:
| Field | Type | Description |
|-------|------|-------------|
| stdout | string | Command standard output |
| stderr | string | Command standard error |
| code | number | Exit code (0 = success) |
Check for errors with an IF node:
{{ $json.code !== 0 }}
Use Continue On Fail on the SSH node so the workflow doesn't halt on non-zero exits — then gate on code manually.
Multi-command tip: Chain commands with && for stop-on-failure or ; to run all regardless:
cd /var/app && git pull origin main && npm install && pm2 restart app
Download File
Pulls a file from the remote server into the workflow as a binary item. Useful for ingesting log files, database dumps, or report exports.
Node settings:
- Operation: Download File
-
Path: absolute path on the remote server (e.g.,
/var/log/app/error.log) -
Binary Property: name to store the file under in
$binary(default:data)
The file lands in the workflow as binary data. Chain it to:
- Spreadsheet File node to parse a CSV
- Move Binary Data to rename the property
- Send Email / Slack to attach and send
- Google Drive / S3 to archive
Upload File
Pushes a binary file from the workflow to a path on the remote server.
Node settings:
- Operation: Upload File
-
Binary Property: the
$binarykey holding the file (default:data) -
Path: destination absolute path on the remote server (e.g.,
/var/app/data/import.csv)
The remote directory must exist. Create it in a preceding Execute Command step:
mkdir -p /var/app/data
Common Patterns
1. Automated Deploy on Schedule
Trigger a deploy every weekday at 3 AM without touching a CI/CD pipeline.
Nodes: Schedule Trigger → SSH (git pull + restart) → IF (exit code check) → Slack (success/failure alert)
# Command
cd /var/www/myapp && git pull origin main && npm ci && pm2 restart myapp
If code !== 0, the IF node routes to a Slack error message with $json.stderr.
2. Log Harvesting and Alerting
Pull the last 100 lines of an application log and scan for ERROR keywords.
Nodes: Schedule Trigger → SSH (Execute Command: tail -100 /var/log/app/app.log) → Code node (filter lines containing "ERROR") → IF (errors found?) → Slack alert
// Code node: extract error lines
const lines = $json.stdout.split('\n');
const errors = lines.filter(l => l.includes('ERROR'));
return [{ json: { errors, count: errors.length } }];
3. Remote File Export and Archive
Run a database dump on the remote server, download the file, and archive it to S3.
Nodes:
-
SSH (Execute Command):
mysqldump -u root -p"$DB_PASS" mydb > /tmp/backup_$(date +%Y%m%d).sql -
SSH (Execute Command):
ls /tmp/backup_*.sql | tail -1→ capture filename in stdout - SSH (Download File): path from step 2's stdout
-
S3 (Upload): push binary to
backups/prefix -
SSH (Execute Command):
rm /tmp/backup_*.sql→ cleanup
Gotchas
| Issue | Root Cause | Fix |
|---|---|---|
All configured authentication methods failed |
Wrong key type or passphrase | Check OpenSSH format; verify passphrase; test with ssh -i key user@host manually |
ECONNREFUSED |
Port 22 blocked or sshd not running | Check firewall rules and systemctl status sshd
|
| Commands run but environment variables missing | SSH non-interactive shell doesn't load .bashrc
|
Source explicitly: bash -lc 'your command' or use full paths (e.g., /usr/local/bin/node) |
sudo commands hang |
sudo needs a TTY | Either add NOPASSWD to sudoers or use `echo 'pass' |
| File download truncated | Large file, timeout | Compress before downloading: {% raw %}gzip /tmp/backup.sql then download .gz; decompress with n8n Compression node |
Path does not exist on Upload |
Remote directory missing | Add an Execute Command step to mkdir -p /your/path before uploading |
| Binary property name mismatch | Upload looking for wrong key | Confirm Binary Property matches what the previous node outputs (inspect with Move Binary Data) |
Security Notes
-
Never hardcode passwords or passphrases in the Command field — they appear in execution logs. Store secrets in n8n Variables (
$vars.DB_PASS) or environment variables and reference them. -
Least privilege: Create a dedicated
n8n-botuser on the remote server with only the permissions it needs. Avoid running workflows asroot. - Audit trail: Consider logging each SSH command execution to a Google Sheet or database for compliance.
- Key rotation: Rotate the n8n SSH key on the same schedule as your other service keys.
Free Workflow JSON
Import this starter workflow: Schedule → SSH deploy → exit code check → Slack alert.
{
"name": "SSH Deploy + Slack Alert",
"nodes": [
{
"parameters": { "rule": { "interval": [{ "field": "cronExpression", "expression": "0 3 * * 1-5" }] } },
"name": "Schedule Trigger",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [240, 300]
},
{
"parameters": {
"operation": "executeCommand",
"command": "cd /var/www/myapp && git pull origin main && npm ci --production && pm2 restart myapp"
},
"name": "SSH Deploy",
"type": "n8n-nodes-base.ssh",
"credentials": { "sshPassword": { "id": "1", "name": "My SSH Credential" } },
"position": [460, 300]
},
{
"parameters": { "conditions": { "number": [{ "value1": "={{ $json.code }}", "operation": "equal", "value2": 0 }] } },
"name": "IF Success",
"type": "n8n-nodes-base.if",
"position": [680, 300]
},
{
"parameters": { "text": "✅ Deploy succeeded at {{ $now.toISO() }}" },
"name": "Slack OK",
"type": "n8n-nodes-base.slack",
"position": [900, 200]
},
{
"parameters": { "text": "❌ Deploy FAILED (exit {{ $json.code }}):\n{{ $json.stderr }}" },
"name": "Slack Error",
"type": "n8n-nodes-base.slack",
"position": [900, 400]
}
],
"connections": {
"Schedule Trigger": { "main": [[{ "node": "SSH Deploy", "type": "main", "index": 0 }]] },
"SSH Deploy": { "main": [[{ "node": "IF Success", "type": "main", "index": 0 }]] },
"IF Success": {
"main": [
[{ "node": "Slack OK", "type": "main", "index": 0 }],
[{ "node": "Slack Error", "type": "main", "index": 0 }]
]
}
}
}
Want more? The n8n Workflow Starter Pack includes 10 production-ready workflows covering deploy automation, log monitoring, database backups, and more — all with credentials wired up and error handling included. $29, one-time.
Summary
| Operation | Use case |
|---|---|
| Execute Command | Deploy code, restart services, run scripts, harvest logs |
| Download File | Pull logs, exports, database dumps into the workflow |
| Upload File | Push CSVs, configs, or generated reports to a remote server |
The SSH node is one of the most powerful nodes in n8n for self-hosted infrastructure automation. Combine it with Schedule Trigger for unattended deploys, with Slack/Email for alerts, and with S3/Drive for off-server archiving.
What are you running via SSH in your workflows? Drop a comment below — I read every one.
Top comments (1)
Are you using the SSH node for deploys, log harvesting, or DB dumps? Would love to hear what remote automation you've wired up — drop your use case below.