Infrastructure

ComponentAddressNotes
n8n server54.151.191.198Amazon Linux, runs as node user inside Docker
ev-prod-vpn18.139.20.216Ubuntu AMI, user: ubuntu, IP whitelisted by TNG
TNG SFTPsftp.tngdigital.com.my:10086Retains files for ~30 days only

Problem

TNG provides settlement reports via SFTP with IP whitelisting. The n8n server has no static IP. Solution: route through ev-prod-vpn (whitelisted jump host).

Key insight: Docker user context

n8n runs as the node user inside its container, not as ec2-user on the host. The container has its own isolated filesystem — /home/ec2-user/ doesn’t exist inside it.

When n8n executes an SSH command, it runs as node inside the container. The SSH client therefore looks for keys at node’s home directory: /home/node/.ssh/. A key placed at /home/ec2-user/.ssh/ on the EC2 host is invisible to it. See ssh-how-it-works.

  • SSH keys must be in /home/node/.ssh/ (inside the container), not /home/ec2-user/.ssh/ (on the host)
  • File paths must exist inside the container filesystem
  • docker cp is required to move files between host and container
  • ev-prod-vpn runs Ubuntu, so the SSH user is ubuntu, not ec2-user

Warning

ev-prod-vpn user is ubuntu, not ec2-user — Amazon Linux uses ec2-user but ev-prod-vpn runs Ubuntu. Using the wrong user causes silent SSH authentication failure.

Working solution

Transfer command (runs via SSH to ev-prod-vpn)

echo -e "cd {folder}\nget {filename} /tmp/{filename}\nbye" | \
  sshpass -p '{password}' sftp -o StrictHostKeyChecking=no -P {port} {user}@{host} && \
  scp /tmp/{filename} n8n:/tmp/{filename} && \
  ssh n8n "docker cp /tmp/{filename} n8n:/home/node/.n8n-files/{filename}"

Three steps in one command:

  1. SFTP download: TNG → ev-prod-vpn /tmp/
  2. SCP: ev-prod-vpn /tmp/ → n8n host /tmp/
  3. docker cp: n8n host /tmp/ → n8n container /home/node/.n8n-files/

SSH host alias on ev-prod-vpn

# ~/.ssh/config on ev-prod-vpn
Host n8n
  HostName 54.151.191.198
  User ec2-user
  IdentityFile ~/.ssh/vpn-to-n8n

Workflow nodes

Schedule (9 AM daily)
  → Set Variables (dates, paths, credentials)
  → SSH to ev-prod-vpn (3-step command above)
  → Read/Write Files from Disk (/home/node/.n8n-files/{filename})
  → Upload to Google Drive (TnG Settlements Shared Drive)
  → Delete Temporary File (docker exec rm)

SSH key persistence

The SSH key must be volume-mounted into the container — keys copied in via docker cp are lost on container restart.

-v /home/ec2-user/.ssh/n8n-key:/home/node/.ssh/id_rsa:ro

Full docker run is saved in ~/run_docker.sh on the n8n host.

Google Drive: Shared Drive, not My Drive

Service accounts created after April 15, 2025 have zero storage quota on personal My Drive. Files must be uploaded to a Shared Drive instead. Add the service account as a Contributor to the Shared Drive.

Turn off the “Impersonate a User” option in the n8n credential — it requires Domain-wide Delegation, which is not configured.

Warning

Post-April-2025 service accounts cannot upload to My Drive — use a Shared Drive and add the service account as Contributor.

What didn’t work

AttemptFailure
n8n native SFTP node with ProxyJumpSFTP library doesn’t read ~/.ssh/config
SSH tunnel + n8n SFTP nodeFile path access issues across Docker/host boundary
cat file to stdoutEncoding issues with large CSVs
Read/Write Files from Disk reading /tmpn8n 2.0 breaking change: file access restricted to /home/node/.n8n-files by default
Google service account uploading to My Drive403 Forbidden — zero Drive quota for post-April-2025 service accounts
”Impersonate a User” in n8n credentialunauthorized_client — requires Domain-wide Delegation
SSH key copied in via docker cpKey lost on container restart — must be volume-mounted

Warning

n8n 2.0 breaking change: file access restricted to /home/node/.n8n-files/tmp is no longer accessible from Read/Write Files from Disk. This can surface silently when a container is recreated and loses a previous N8N_RESTRICT_FILE_ACCESS_TO override.

Operational risks

  • EC2 instance termination — Docker named volumes (n8n_data, n8n_files) and the SSH key live on the instance’s local disk. A restart is safe (--restart=always), but termination loses everything including workflows, credentials, and keys. Consider regular EBS snapshots or AMI backups.