ssh, scp & curl
Log into a remote GPU box, copy files to it, and hit a REST API — all from the terminal using ssh, scp, and curl.
What you'll learn
- How to open a shell on a remote machine with ssh and authenticate using key pairs
- How to copy files between machines with scp, and when to prefer rsync
- How to make HTTP requests from the terminal with curl and wget
Before you start
The last chapter ended by naming the terminal’s real superpower — reach — and promised the tools that deliver it. Here they are: ssh to open a shell on a machine anywhere in the world, scp to carry files across to it, and curl to talk to any web service with no browser at all.
Why these three tools belong together
ssh (Secure Shell) gives you an encrypted interactive shell on any remote machine you have access to — as if you were sitting in front of it. Once you are logged in, scp (Secure Copy) lets you move files between your laptop and that machine over the same encrypted channel. And curl (Client URL) lets you speak HTTP directly from the terminal, which is how you call REST APIs, download files, and inspect web services. Together they cover almost every “talk to a server” task in data and AI work.
ssh — opening a shell on a remote machine
The basic command is:
ssh user@host
Replace user with your login name on the remote machine and host with its IP address or hostname.
ssh ubuntu@203.0.113.42
After connecting, your prompt changes to show the remote machine’s hostname. Everything you type now runs there, not on your laptop. Type exit (or press Ctrl-D) to disconnect.
Choosing a port
By default ssh listens on port 22. If the server uses a different port, pass -p:
ssh -p 2222 ubuntu@203.0.113.42
Running a single command without opening an interactive shell
Append the command in quotes:
ssh ubuntu@203.0.113.42 "nvidia-smi"
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 535.86.05 Driver Version: 535.86.05 CUDA Version: 12.2 |
|-------------------------------+----------------------+----------------------+
| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC |
...
The connection closes as soon as the command finishes.
SSH key authentication — why keys beat passwords
When you connect with a password, you type it every time and it travels over the network (even though it is encrypted). An SSH key pair is better: you prove who you are with mathematics instead of a secret string.
A key pair has two files:
- Private key — stays on your laptop, never leaves. Think of it as your house key.
- Public key — copied to every server you want to access. Think of it as the lock. The lock can only be opened by your key, but owning the lock does not help anyone open it.
Generating a key pair
ssh-keygen -t ed25519 -C "your@email.com"
-t ed25519 selects the modern Edwards-curve algorithm (faster and more secure than the older RSA default). -C adds a comment so you know which key this is.
You will be prompted for a passphrase — a local password that encrypts the private key on disk. Use one. If someone steals the file, the passphrase stops them from using it.
The files land in ~/.ssh/:
~/.ssh/id_ed25519 ← private key (keep this secret)
~/.ssh/id_ed25519.pub ← public key (safe to share)
Installing your public key on the server
ssh-copy-id ubuntu@203.0.113.42
ssh-copy-id appends your public key to ~/.ssh/authorized_keys on the remote machine. After that, ssh ubuntu@203.0.113.42 no longer asks for a password — it authenticates with your key pair automatically.
If ssh-copy-id is not available (some macOS setups), you can do it manually:
cat ~/.ssh/id_ed25519.pub | ssh ubuntu@203.0.113.42 "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"
A diagram of the connection
scp — copying files over SSH
scp (Secure Copy Protocol) uses the same encrypted channel as SSH to copy files. The syntax mirrors cp but every path can be prefixed with user@host: to target the remote machine.
Upload a file to the server
scp dataset.csv ubuntu@203.0.113.42:/home/ubuntu/data/
Download a file from the server
scp ubuntu@203.0.113.42:/home/ubuntu/results/output.json ./results/
Copy a whole directory with -r
scp -r ./my-project ubuntu@203.0.113.42:/home/ubuntu/
When to use rsync instead
scp copies everything every time. If you are syncing a large dataset and the transfer drops halfway, you start from scratch. rsync only transfers the difference:
rsync -avz ./data/ ubuntu@203.0.113.42:/home/ubuntu/data/
-aarchive mode: preserves permissions, timestamps, symlinks-vverbose: shows each file-zcompress data in transit
Use scp for quick one-off copies. Use rsync for anything large or repeated.
curl — making HTTP requests from the terminal
curl (Client URL) sends HTTP requests and prints the response. It is the standard tool for testing APIs, downloading files, and inspecting HTTP headers.
Download a URL
curl https://api.github.com/repos/astro-build/astro
This prints the JSON response body to stdout.
Save to a file with -o
curl -o astro.json https://api.github.com/repos/astro-build/astro
Follow redirects with -L
Many URLs redirect (HTTP 301/302) before landing on the final resource. Without -L, curl stops at the redirect and prints a short HTML page. With it, curl follows automatically:
curl -L -o model.tar.gz https://short.link/model-weights
See only the response headers with -I
-I sends a HEAD request — the server returns headers but no body. Useful to check content-type, content-length, or caching metadata before committing to a full download:
curl -I https://api.github.com/repos/astro-build/astro
HTTP/2 200
content-type: application/json; charset=utf-8
x-ratelimit-remaining: 59
...
Send a custom header with -H
APIs often require an Authorization header:
curl -H "Authorization: Bearer $MY_TOKEN" https://api.example.com/data
Use a shell variable ($MY_TOKEN) rather than pasting the token directly into your history.
POST JSON data with -X POST -d
curl -X POST \
-H "Content-Type: application/json" \
-d '{"model": "gpt-4o", "prompt": "Hello"}' \
https://api.example.com/v1/completions
Newer versions of curl accept --json as a shorthand that sets the content-type header and selects POST automatically:
curl --json '{"model": "gpt-4o", "prompt": "Hello"}' \
https://api.example.com/v1/completions
A realistic AI inference call
curl -X POST \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"What is embeddings?"}]}' \
https://api.openai.com/v1/chat/completions
{
"id": "chatcmpl-abc123",
"object": "chat.completion",
"choices": [
{
"message": {
"role": "assistant",
"content": "Embeddings are numerical representations of text ..."
}
}
]
}
wget — the simpler download tool
wget is narrower than curl: it downloads URLs and saves them to files. It is pre-installed on most Linux servers and handles resuming broken downloads with -c:
wget https://example.com/large-dataset.tar.gz
wget -c https://example.com/large-dataset.tar.gz
Use curl when you need headers, POST data, or API calls. Use wget when you just want a file and want automatic retry built in.
Quick reference
| Task | Command |
|---|---|
| Open a remote shell | ssh user@host |
| Run one command remotely | ssh user@host "nvidia-smi" |
| Generate an SSH key | ssh-keygen -t ed25519 -C "you@email.com" |
| Install public key on server | ssh-copy-id user@host |
| Upload a file | scp local.csv user@host:/remote/path/ |
| Download a file | scp user@host:/remote/file.csv ./ |
| Copy a directory | scp -r ./dir user@host:/remote/ |
| Sync a directory (resumable) | rsync -avz ./dir/ user@host:/remote/ |
| Fetch a URL | curl URL |
| Save to file | curl -o file.json URL |
| Follow redirects | curl -L URL |
| Headers only | curl -I URL |
| Add a header | curl -H "Authorization: Bearer $TOKEN" URL |
| POST JSON | curl --json '{"key":"val"}' URL |
| Simple download | wget URL |
In one breath
Three tools cover almost every “talk to a server” task. ssh user@host opens an encrypted shell on a remote machine (-p for a non-default port, a quoted command at the end to run just one thing and disconnect). Authenticate with a key pair, not a password: ssh-keygen -t ed25519 makes a private key that never leaves your laptop and a public key you install on the server with ssh-copy-id. scp copies files over that same channel (-r for directories), and rsync -avz is better for anything large or repeated because it sends only the difference. curl speaks HTTP from the terminal — -o to save, -L to follow redirects, -I for headers only, -H to add an auth header, --json/-d to POST — which is exactly how you test a REST or AI inference API. Two safety rules: never share the private key, and never blind-pipe curl ... | bash.
Practice
Before the quiz, script the cloud-GPU workflow in your head, command by command: you have a fresh server at ubuntu@203.0.113.42. (a) What is the first thing you set up so you stop typing a password every connection, and which of the two key files goes where? (b) Upload your local data/ folder. (c) Once training finishes, fetch results/metrics.json back. (d) POST a test prompt to the inference endpoint with a bearer token read from an environment variable, not pasted inline — why does that last detail matter?
Quick check
A question to carry forward
Look at what that practice workflow actually was: a sequence — key setup, upload, train, fetch, test — five or six commands you would run in the same order every single time you spin up a box. Typing them by hand, every time, is both tedious and exactly how a 203.0.113.42 quietly becomes a 203.0.113.43 and your dataset lands on a stranger’s server. The obvious move is to write the sequence down once, in a file, and run the whole thing by name. But a genuinely useful script is more than a list of commands glued together — it takes arguments (which server? which dataset?), makes decisions (stop if the upload failed), and repeats (do this for all ten files). Turning a fragile string of typed commands into a sturdy, reusable little program is the next lesson: shell scripting.