Skip to content

azlin cp

Copy files and directories between local machine and azlin VMs with bidirectional transfer support.

Description

The azlin cp command provides secure file transfer using rsync over SSH. Transfer files to VMs, download results from VMs, or copy between VMs directly. Security filters automatically block sensitive files like SSH keys and credentials.

Usage

azlin cp SOURCE DESTINATION [OPTIONS]

Arguments

  • SOURCE - Source file/directory (local, vm:path, or ip:path)
  • DESTINATION - Destination path (local, vm:path, or ip:path)

Options

Option Description
-r, --recursive Copy directories recursively
--dry-run Preview what would be copied
--exclude PATTERN Exclude files matching pattern
--resource-group, --rg TEXT Azure resource group (for VM name resolution)
-h, --help Show help message

Examples

Copy File to VM

# Copy local file to VM
azlin cp report.pdf my-vm:~/documents/

Output:

Copying to my-vm (20.12.34.56)...
  Source: /Users/ryan/report.pdf
  Destination: /home/azureuser/documents/report.pdf

Transferring...
report.pdf: 100% [===================] 2.4 MB/s

✓ Transfer complete!
  Files: 1
  Size: 2.4 MB
  Time: 1.2s

Copy File from VM

# Download file from VM to local
azlin cp my-vm:~/results.tar.gz ./

Copy Directory Recursively

# Copy entire directory to VM
azlin cp -r ./my-project/ my-vm:~/workspace/

Output:

Copying to my-vm (20.12.34.56)...
  Source: /Users/ryan/my-project/
  Destination: /home/azureuser/workspace/my-project/

Scanning directory...
  Files found: 247
  Total size: 15.3 MB

Transferring...
████████████████████████████████ 100%

✓ Transfer complete!
  Files: 247
  Size: 15.3 MB
  Time: 8.4s
  Speed: 1.8 MB/s

Preview Transfer (Dry Run)

# See what would be copied without actually copying
azlin cp --dry-run large-dataset.zip my-vm:~/

Output:

DRY RUN - No files will be copied

Would transfer:
  large-dataset.zip (450 MB)
  From: /Users/ryan/large-dataset.zip
  To: my-vm:/home/azureuser/large-dataset.zip

Estimated time: 3m 45s (at 2 MB/s)

Copy Between VMs

# Copy directly between two VMs
azlin cp vm1:~/data.csv vm2:~/backup/

Exclude Patterns

# Copy project but exclude node_modules
azlin cp -r ./myapp/ my-vm:~/workspace/ --exclude 'node_modules' --exclude '.git'

Copy Using IP Address

# Copy using direct IP (no VM name resolution)
azlin cp report.pdf 20.12.34.56:~/

Common Workflows

Deploy Application Code

# Build locally
npm run build

# Copy to VM
azlin cp -r ./dist/ my-vm:~/app/dist/

# Restart service
azlin connect my-vm
sudo systemctl restart myapp

Download Training Results

# Copy trained model from VM
azlin cp my-vm:~/ml-training/model.pkl ./models/

# Download logs
azlin cp my-vm:~/ml-training/training.log ./logs/

Backup VM Data

# Download important data
azlin cp -r my-vm:~/projects/ ./backups/vm-projects-$(date +%Y%m%d)/
azlin cp -r my-vm:~/data/ ./backups/vm-data-$(date +%Y%m%d)/

Multi-VM Distribution

# Deploy config to all VMs
for vm in $(azlin list --format json | jq -r '.[].name'); do
  echo "Deploying to $vm..."
  azlin cp ./config.yaml $vm:~/app/config.yaml
done

Sync Datasets

# Copy large dataset to training VMs
azlin cp -r ./datasets/ vm1:~/ml-data/
azlin cp -r ./datasets/ vm2:~/ml-data/
azlin cp -r ./datasets/ vm3:~/ml-data/

Security Filters

Automatically blocks these sensitive files:

SSH Keys

  • *.pem, *.key
  • id_rsa, id_rsa.pub, id_ed25519
  • .ssh/*

Cloud Credentials

  • .aws/credentials, .azure/credentials
  • credentials.json, service-account.json
  • .config/gcloud/*

Environment Files

  • .env, .env.*, .env.local, .env.production

Git Internals

  • .git/ (use --exclude .git to be explicit)

Bypass warning:

⚠ WARNING: Blocked sensitive files:
  .env
  .ssh/id_rsa

Sensitive files not transferred. Use --force to override (not recommended).

Performance

File Size Transfer Time (estimate)
1 MB 0.5-1 second
10 MB 5-10 seconds
100 MB 50-60 seconds
1 GB 8-10 minutes

Varies by network speed and VM region

Optimization Tips

# Use compression for large files
gzip large-file.txt
azlin cp large-file.txt.gz my-vm:~/

# Exclude unnecessary files
azlin cp -r ./project/ my-vm:~/ \
  --exclude 'node_modules' \
  --exclude '.git' \
  --exclude '*.log'

# Transfer archives instead of many small files
tar -czf project.tar.gz ./project/
azlin cp project.tar.gz my-vm:~/

Troubleshooting

Permission Denied

Problem: "Permission denied" error.

Solution:

# Verify destination is writable
azlin connect my-vm
ls -la ~/destination-path

# Use home directory first
azlin cp file.txt my-vm:~/
# Then move with sudo
azlin connect my-vm
sudo mv ~/file.txt /opt/app/

Connection Timeout

Problem: Transfer times out on large files.

Solution:

# Use compression
gzip large-file.dat
azlin cp large-file.dat.gz my-vm:~/

# Or split large files
split -b 100M huge-file.dat chunk-
for chunk in chunk-*; do
  azlin cp $chunk my-vm:~/
done

# Reassemble on VM
azlin connect my-vm
cat chunk-* > huge-file.dat

Blocked Sensitive Files

Problem: Files not transferred due to security filters.

Solution:

# Review blocked files
azlin cp --dry-run ./project/ my-vm:~/

# Exclude sensitive files explicitly
azlin cp -r ./project/ my-vm:~/ --exclude '.env'

# For legitimate need to transfer secrets (use with caution)
# Consider using azlin env set instead
azlin env set my-vm API_KEY="value"

Comparison with SCP

Feature azlin cp scp
VM name resolution ✗ (IP only)
Session name support
Security filters
Dry-run mode
Progress bar Limited
Recursive by default With -r With -r

Examples by Use Case

Web Development

# Deploy frontend build
npm run build
azlin cp -r ./dist/ web-vm:~/app/public/

# Update API code
azlin cp -r ./api/ api-vm:~/app/api/

Data Science

# Upload training data
azlin cp -r ./datasets/ ml-vm:~/data/

# Download trained models
azlin cp -r ml-vm:~/models/ ./trained-models/

# Get training logs
azlin cp ml-vm:~/training.log ./logs/

DevOps

# Distribute configuration
for vm in $(azlin list --tag app=api --format json | jq -r '.[].name'); do
  azlin cp ./nginx.conf $vm:/tmp/nginx.conf
  azlin connect $vm "sudo mv /tmp/nginx.conf /etc/nginx/nginx.conf && sudo systemctl reload nginx"
done

Azure Bastion Support

azlin cp automatically detects and uses Azure Bastion for VMs without public IP addresses, providing the same seamless experience as azlin connect. No configuration or special flags required.

How It Works

When you copy files to/from a VM, azlin cp:

  1. Checks for public IP - If VM has public IP, uses direct connection (fastest)
  2. Auto-detects Bastion - If no public IP, searches for Azure Bastion in resource group
  3. Creates secure tunnel - Automatically establishes tunnel through Bastion (localhost:50000-60000)
  4. Transfers files - Routes rsync through tunnel transparently
  5. Cleans up - Automatically closes tunnel when transfer completes

No user action required - Works identically to VMs with public IPs.

Bastion Examples

Copy to Bastion-Only VM

# VM has no public IP, but Bastion exists in resource group
azlin cp dataset.tar.gz secure-vm:~/data/

Output:

VM secure-vm has no public IP, checking for Bastion...
✓ Found Bastion: azlin-bastion-eastus
Creating Bastion tunnel...
✓ Bastion tunnel established: azlin-bastion-eastus -> 127.0.0.1:52341

Copying to secure-vm (via Bastion)...
  Source: /Users/ryan/dataset.tar.gz
  Destination: /home/azureuser/data/dataset.tar.gz

Transferring...
dataset.tar.gz: 100% [===================] 2.1 MB/s

✓ Transfer complete!
  Files: 1
  Size: 450 MB
  Time: 3m 35s
  Connection: Bastion (azlin-bastion-eastus)

Cleaning up Bastion tunnel...
✓ Tunnel closed

Deploy to Zero-Trust Environment

# All VMs in production have no public IPs
azlin cp -r ./app/ prod-api-1:~/app/
azlin cp -r ./app/ prod-api-2:~/app/
azlin cp -r ./app/ prod-api-3:~/app/

# Bastion automatically used for all transfers
# No configuration changes needed

Performance Comparison

Connection Type Typical Speed Use Case
Direct (public IP) 10-50 MB/s Development VMs, temporary instances
Bastion tunnel 2-20 MB/s Production VMs, compliance requirements

Notes: - Bastion adds 10-30% overhead vs direct connection - Regional Bastion (same region as VM) performs best - Cross-region Bastion adds additional latency - Security benefits often outweigh performance impact

Bastion Troubleshooting

No Bastion Found

Problem: "VM has no public IP and no bastion is available"

Solution:

# Check if Bastion exists in resource group
az network bastion list \
  --resource-group your-resource-group \
  --query "[].{name:name, location:location, state:provisioningState}" \
  --output table

# If no Bastion exists, see setup guide:
# https://rysweet.github.io/azlin/bastion/setup/

Slow Transfer via Bastion

Problem: Transfer much slower than expected

Solutions:

# 1. Check Bastion and VM are in same region
az vm show --resource-group rg --name vm --query location
az network bastion show --resource-group rg --name bastion --query location

# 2. Use compression for large files
gzip large-file.dat
azlin cp large-file.dat.gz vm:~/

# 3. Use archives instead of many small files
tar -czf project.tar.gz ./project/
azlin cp project.tar.gz vm:~/
azlin connect vm "tar -xzf ~/project.tar.gz"

See Also