Zeabur Server SSH + kubectl
Always use
npx zeabur@latestto invoke Zeabur CLI. Never usezeaburdirectly or any other installation method.
Run commands on a user's dedicated server, and use kubectl to debug Kubernetes workloads on servers that run ZeaburOS.
Check which kind of server you are on before using kubectl
- ZeaburOS servers run k3s with kubectl pre-installed. Everything in this skill applies.
- Ubuntu — a rented server that is not running ZeaburOS. It has
no k3s and no kubectl; every
kubectlcommand below fails withcommand not found.server execstill works for ordinary shell commands.
Renting provisions Ubuntu only, so never assume kubectl exists.
Read the machine's kind from the CLI — do not SSH in to work it out:
npx zeabur@latest server get <server-id> -i=false --json | jq -r .os
ZeaburOS or Ubuntu. server list reports the same thing for every server at
once, as an OS column and an os field.
Needs CLI 0.21.0 or newer. If
oscomes backnull, the CLI is older — either letnpx zeabur@latestfetch the current version, or fall back to the API (see thezeabur-server-rentskill for the token setup that defines$ZAPI_CFG):curl -sS --max-time 30 -K "$ZAPI_CFG" https://api.zeabur.com/graphql \ -H "Content-Type: application/json" \ -d '{"query":"query($id: ObjectID!) { server(_id: $id) { hasK3s } }","variables":{"id":"<server-id>"}}'
hasK3sis three-state and the third state is a trap:trueis ZeaburOS,falseis Ubuntu only, andnullis a server predating the field — which does run ZeaburOS. So the test ishasK3s === false, never!hasK3s; the latter sweeps in every older server and wrongly reports it as a plain VPS.
If you did not check first and a kubectl command fails with command not found, that is the same signal — tell the user the server is a plain Ubuntu VPS
instead of retrying the command. Installing ZeaburOS is what adds kubectl; the
zeabur-server-rent skill covers how.
One exception. All of the above answers what kind of machine this is. If
the user asks specifically whether some binary is present, check the machine —
a ZeaburOS server always has kubectl, but a plain Ubuntu VPS may have had it
installed by its owner, and the OS field cannot tell you that.
Run a command: server exec (recommended)
Run a command on the server in one step. The CLI fetches the credentials and opens
the connection internally, so you never handle the password — passwords with
special characters just work, and no ssh2/sshpass is needed.
npx zeabur@latest server exec --id <server-id> -- <command>
Examples:
# Single command
npx zeabur@latest server exec --id <server-id> -- sudo kubectl get pods -A -o wide
# Compound command — quote it as ONE argument so && / | stay intact
npx zeabur@latest server exec --id <server-id> -- 'echo "=== PODS ===" && sudo kubectl get pods -A && echo "=== EVENTS ===" && sudo kubectl get events -A --sort-by=.lastTimestamp | tail -20'
Notes:
- Everything after
--is the remote command, joined likessh host <command>. Quote a compound command (with&&/|/ redirects) as a single argument. - stdout/stderr stream live; the remote command's exit code is propagated
(a pipeline reports only its last stage's status —
… | tailhides an upstream failure, so don't rely on the exit code across a pipe). - If you don't know the server ID, list servers first:
(or use thenpx zeabur@latest server list -i=falsezeabur-server-listskill).
Common kubectl Commands
ZeaburOS servers only. On a plain Ubuntu VPS these all fail with
kubectl: command not found— see the section above.
Pass any of these as the command to server exec. Always use sudo kubectl —
the SSH user may not have direct access to the k3s kubeconfig. Any command with a
pipe (|) or && must be quoted as a single argument, or the local shell
splits it and runs part locally — e.g.
server exec --id <id> -- 'sudo kubectl top pods -A --sort-by=memory | head -20'.
| Task | Command |
|---|---|
| List all pods | sudo kubectl get pods -A -o wide |
| Problem pods only | sudo kubectl get pods -A --field-selector=status.phase!=Running,status.phase!=Succeeded |
| Pod logs | sudo kubectl logs <pod-name> -n <namespace> --tail=100 |
| Exec into container | sudo kubectl exec <pod-name> -n <namespace> -- <command> |
| Node resources | sudo kubectl top nodes |
| Pod resources | sudo kubectl top pods -A --sort-by=memory | head -20 |
| Describe pod | sudo kubectl describe pod <pod-name> -n <namespace> |
| Recent events | sudo kubectl get events -A --sort-by=.lastTimestamp | tail -30 |
| Restart deployment | sudo kubectl rollout restart deployment/<name> -n <namespace> |
Fallback: manual SSH (only if server exec is unavailable)
Use this only if server exec isn't available (e.g. an older CLI). Otherwise
prefer server exec above — this path is fragile with special-character passwords.
Step 1: Get SSH credentials
npx zeabur@latest server ssh-info --id <server-id> -i=false
Output is JSON: {"ip":"1.2.3.4","port":22,"username":"root","password":"xxx"}
Step 2: Connect
Use the Node.js ssh2 method by default; use sshpass only when its availability
is already known. Do NOT run which sshpass to check — it wastes a step where it's
never installed.
# sshpass (only if already known to be available)
sshpass -p '<password>' ssh -o StrictHostKeyChecking=no -p <port> <username>@<ip> sudo kubectl get pods -A
# Node.js ssh2 (the Zeabur agent sandbox has it pre-installed)
NODE_PATH=$([ -d /root/.global/node_modules ] && echo /root/.global/node_modules || echo /home/vercel-sandbox/.global/node_modules) node -e "
const {Client} = require('ssh2');
const c = new Client();
c.on('ready', () => {
c.exec('<command>', (err, stream) => {
if (err) { console.error(err); process.exit(1); }
let out = '', errOut = '';
stream.on('data', d => out += d);
stream.stderr.on('data', d => errOut += d);
stream.on('close', code => {
if (out) console.log(out);
if (errOut) console.error(errOut);
c.end();
process.exit(code);
});
});
}).connect({host:'<ip>', port:<port>, username:'<username>', password:'<password>'});
"
Tips
- Combine commands: batch related checks with
&&in a singleserver execcall to reduce round trips. - Use
-o wide: adds node name and IP to pod listings, useful for scheduling issues. - Namespace matters: Zeabur services usually run in non-default namespaces. Use
-A(all namespaces) first to locate the right one, then scope with-n <namespace>. - Read project docs first: if a fix attempt fails, exec into the container and
check README/config before blindly checking metrics:
sudo kubectl exec <pod> -n <ns> -- cat /app/README.md - To find server IDs, use the
zeabur-server-listskill. For simpler container commands that don't need server-level access, use thezeabur-service-execskill.