EST. 2026 — Notes from real server work SYSADMIN · SECURITY · AI · DIRECTADMIN Issue 01
THE SYSADMIN ALMANAC
Home / AI / Managing Linux Servers Safely with AI over SSH
AI

Managing Linux Servers Safely with AI over SSH

by Ton-Or ·12 Jun 2026·13 min read
Managing Linux Servers Safely with AI over SSH

I want to write out my actual process in full, from how AI sees my machine to the hard rules before running anything on production. It is a bit long, but covers every workflow.

1-minute summary

Symptom / problem
อยากให้ AI ช่วยดูแล Linux ผ่าน SSH แต่กังวลว่าจะสั่งงานจนระบบเสียหาย
Who should read
ผู้ดูแลเซิร์ฟเวอร์ที่ใช้ Claude ช่วยตรวจสอบและแก้ปัญหาระบบจริง
Fix in short
เริ่มจากอ่านอย่างเดียว สำรองข้อมูล ตรวจสคริปต์ก่อนใช้ และยืนยันผลหลังแก้ทุกครั้ง
Jump to steps

A member of the Claude Thailand group asked how to use AI to audit and fix Linux server security, and how to do it safely without accidentally breaking the system. So I decided to sit down and write out my actual process in full. It is a bit long, but covers every workflow.

This is the method I have used since migrating my server from CentOS 7 to Debian, moving nearly a hundred domains over in a single day with as little downtime as possible. I have already written a series about the actual work I did with this method. You can find it at the end of this article.

Before getting into it, I need to flag one thing because it matters a lot.

Before you start: context window

Never use a model with a context window below 1M. Managing a server over SSH involves a lot of context: command output, logs being reviewed, multiple config files, and verification results at each layer. With a small context window, the model will forget what it read at the start and make bad decisions. Always use the latest Claude model (currently Opus 4.8 with 1M context) for the safest approach.

Now, let's get into it.

1. First, understand how Claude sees your machine

The most important thing to understand first is that Claude does not directly "enter" your server. It only runs commands on your machine (Windows/Mac), and your machine then sends commands to the Linux server over SSH. That means all the tools need to be "on your side."

The overall workflow is the same on Windows and Mac/Linux. Only the "tool sending SSH commands" differs:

Command pathClaudeon your machine->Your machineplink/pscp or ssh/scp->SSHport->Linux serverruns the commands

Both follow the same structure, with only the "tool sending SSH commands" differing — Windows uses PuTTY's plink/pscp (because Windows ssh easily gets stuck on interactive prompts), while Mac/Linux can use native ssh/scp directly. The risks and operating principles after that are identical.

The point to remember is that Claude can only do what your machine can do. If the machine has no plink, it cannot SSH. If python is a stub, it will fail to run scripts. So "preparing the machine" is what defines the full scope of its capabilities and safety.

On Windows, Claude has 2 main tools: PowerShell (for managing Windows, git, npm) and the Bash tool (POSIX-style Git Bash — unix commands, plink, heredoc). They use different syntax. Bash uses $VAR and forward slashes; PowerShell uses $env:VAR and backticks. One thing to watch is that the Bash tool on Windows often consumes backslashes in paths like C:\..., so use forward slashes like C:/... or the prefix MSYS_NO_PATHCONV=1.

2. For Mac/Linux users — what can you skip?

The good news is that most tooling issues here are "Windows problems" that Mac/Linux users do not encounter. If you use macOS or Linux, you already have native ssh/scp/python3/bash. There is no need to install PuTTY, no backslash path problem, and no Store Python trap.

The differences:

  • SSH/SCP Use native ssh/scp. Host keys live in ~/.ssh/known_hosts, and ~/.ssh/config provides a cleaner way to configure aliases/ports/keys.
  • Automatically supplying passwords Use sshpass, but SSH keys (ssh-copy-id) are the better option.
  • Preventing hangs on interactive prompts Use ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new.
  • Python Use python3 directly.

But Mac/Linux users still need to observe all the same security principles (from section 6 onward), with no OS exceptions. For secrets on macOS, watch out for iCloud/Time Machine unintentionally syncing folders containing secrets to the cloud. Claude still runs on your machine and sends SSH commands to the server in the same way. Destructive scripts are just as dangerous.

3. Tools to prepare (mainly Windows)

This is the set I actually use, and I recommend preparing the following.

  • Anaconda (Python) — Run analysis scripts/parse JSON/build reusable tools. Always invoke python by its full path.
  • Node.js + npm — JS tools, Playwright (screenshots/bots).
  • PuTTY (plink + pscp) — Reliable SSH/SCP for automation. Accepts passwords through a flag, supports hostkey pinning, and avoids getting stuck on interactive prompts.
  • Git + Git Bash — The runtime for the Bash tool, version control, heredoc.
  • sshpass — Supply passwords to ssh/scp when some hosts reject key-auth.

The principle for choosing tools: choose ones that "do not ask," because automation must not have interactive prompts (it will hang). This is why I use plink instead of Windows ssh · For API/integration work, build a python tool that reads secrets from a file and can be called repeatedly, without writing a new curl command every time · Scan the machine once and save the results so later sessions can reuse them without searching for or installing tools again.

4. Why plink is better than Windows ssh

The most common beginner mistake is choosing Windows ssh and having automation abruptly hang. Here is a clear comparison.

plink (PuTTY)

Suited to automation

  • Accepts passwords directly through -pw
  • Pins the host key with -hostkey 'SHA256:...' to prevent MITM without hanging on a yes/no prompt
  • Handles bash -s + multiline heredoc cleanly
  • -batch strictly disables interaction
Windows ssh

Easily stuck on prompts

  • Requires manually entering a password, or setting up a key and still encountering prompts
  • Asks "Are you sure?" on the first connection, immediately hanging automation
  • Test-NetConnection gives false-negatives on my machine
  • Use ping.exe or ssh.exe directly to check ports/connectivity instead

The actual command format I use (I have masked the real values):

MSYS_NO_PATHCONV=1 plink -ssh -batch -P <port> -pw '<pass>' \
  -hostkey 'SHA256:...' root@<server-ip> "bash -s" <<'EOF'
  echo "อยู่บน server แล้ว"
  nginx -t
EOF

-batch disables interaction (no hanging allowed) · MSYS_NO_PATHCONV=1 prevents Git Bash from converting /home/... to C:/Program Files/Git/home/....

A plink drawback to know about

The password briefly appears in the process list when using -pw. That is acceptable on a personal machine, but do not use it on a machine with multiple users. Also, plink runs "everything you type." Long heredocs containing rm/systemctl are dangerous without prior review.

5. The Windows Python trap

The classic trap is that bare python on PATH may be a "Microsoft Store stub." Typing python produces nothing or opens the Store, and scripts then fail in confusing ways. Prevent this by always invoking Anaconda's python by its full path.

C:/Users/<user>/anaconda3/python.exe script.py
C:/Users/<user>/anaconda3/envs/<env>/python.exe ...

Also, use forward slashes in Bash paths. If you use C:\Users\... in the Bash tool, it consumes the backslashes and you get command not found · The advantage of conda is that you can isolate environments by task without conflicts, but choosing the wrong env means the libraries you need will be missing. Also, conda activate sometimes does not work in scripts, so invoking the env's python path directly is more reliable.

6. Storing secrets

The golden rule: Claude should not ask for passwords. It should read them from a file, and all passwords should be in one place.

  • One secret file Store password/hostkey/port. It must be gitignored so it does not end up in the repo.
  • Tools that read secrets Read tokens from a file and never print them.
  • Secrets for individual domains/services Store them in separate files.
Secrets leaking through output/logs

Do not let scripts echo passwords or API keys to stdout, because they end up in the chat log. If you need to retrieve values for storage, mask them or write them directly to a secret file. For temporary files on the server, use mktemp + trap to clean them up. Do not write predictably named files in /tmp, because they can be world-readable and other customers on a shared host can read them.

7. Hard rules before running anything

This is the core of it. First comes rule 0, the most important one.

Rule 0 — Research current information, check every link, and create/update skills before starting. Do not work from the AI's old memory, especially for things that change over time (bot/Cloudflare IP ranges, CVEs, config syntax, API endpoints, tool versions). Before acting, you must:

  1. WebSearch for current information.
  2. Open and read every link yourself to confirm that it is an official, current, accurate source, not an old mirror. Do not cite a link without opening it.
  3. Save verified knowledge as a skill.
  4. If an existing skill is wrong/outdated, fix it before proceeding.

Then follow these rules.

  • Always start read-only The first pass is observation only. Do not make changes while exploring. Capture the full state before deciding (audit first, then fix as a separate step).
  • Create a .sh script or python tool before running it Do not fire off ad hoc one-liners that are hard to review. A readable script can be reviewed + rerun + rolled back.
  • Back up before touching anything, every time Copy the files to be changed into a dated folder + write RESTORE.md.
  • Have a validation gate Test config changes before reloading — nginx -t, bash -n, php -l, visudo -c. On failure, auto-revert from the backup.
  • Verify after changes Test actual behavior (check status with curl, csf -g, systemctl is-active) + check that other sites/customers are unaffected.
  • Destructive actions one line at a time Send rm -rf, DROP, restart, csf -ra one command at a time. Check the result before the next line. For apt removals, omit -y so the list can be reviewed first.
  • "Give me the command" means I will run it myself If I say "give me the command," Claude must output it as text for me to run, not SSH in and run it for me. It should run commands itself only for read-only work or when I explicitly say "go ahead."

The principles form a single sequence:

Standard flow for every production changeRead-only->Backup->Script + Gate->Apply->Verify->Persist

Every change affecting production follows this sequence, except small tasks that are easy to reverse and have no side effects.

8. The core: .sh first + always back up

The rule that prevents the most disasters is to back up before touching anything, with RESTORE.md. The pattern I actually use is a dated folder + saving "state," not just files.

B=/root/<งาน>-backup-$(date +%F)
mkdir -p "$B" && chmod 700 "$B"
cp -a /etc/csf/csf.conf /etc/nginx/nginx-includes.conf "$B"/
iptables-save > "$B"/iptables.txt
ipset list -t > "$B"/ipset.txt
nginx -T > "$B"/nginx-full.txt
cat > "$B"/RESTORE.md <<'EOF'
ย้อนข้อ 1: csf -dr <ip>
ย้อนทั้งหมด: cp csf.conf /etc/csf/ ; csf -ra ; systemctl restart lfd
EOF

Why save "state" too? Because firewall/ipset/iptables state does not live directly in files. Without a snapshot, the rollback will be incomplete.

Script + Validation Gate + Auto-revert:

F=/etc/nginx/nginx-includes.conf
cp "$F" "$F.bak-$(date +%F-%H%M)"
cat >> "$F" <<'EOF'
... config ใหม่ ...
EOF
if nginx -t 2>&1 | grep -q successful; then
    nginx -s reload && echo reloaded
else
    echo FAIL revert; cp "$F.bak-"* "$F"
fi

For repeated tasks, build a python tool, keep the source in the workspace, and deploy the working copy to /usr/local/sbin/. A good tool is idempotent (rerunning it does not break anything) + fail-safe (uses the old cache if it cannot fetch data) + writes logs. For example, I built one that fetches Googlebot/Bingbot IPs from official sources and automatically unbans them, preventing the firewall from banning bots and damaging SEO.

Back up fileDated .bakWrite configAppend to fileChecknginx -tnginx -s reloadPass: continuecp .bak backFail: restore
Config changes need both a backup file and a validation gate. Reload on success; automatically revert on failure.

9. How to use TODOs to keep work under control

For tasks with multiple steps, especially production changes, have Claude create a TODO list first, then work through it one item at a time, with only one item in_progress at any moment. This keeps the plan + progress visible throughout and prevents losing track. The template I use:

  • Back up every config to be touched + RESTORE.md.
  • Fix point A with a validation gate.
  • Fix point B.
  • Verify everything, at every layer + check that other sites are unaffected.
  • Leave risky tasks/difficult decisions until last (such as enabling/disabling country-block).
  • Persist (self-heal to prevent DA/CustomBuild from removing changes) + log.
  • Update memory + summary report.

The trick is to say, "Create TODOs for all the fixes, then back up for restoration and arrange the work step by step," and specify which items you want done last. Claude will order them for you.

10. Dangers & traps I have actually encountered

  • Bash consumes backslashes — C:\... leads to command not found. Fix it with forward slashes or MSYS_NO_PATHCONV=1.
  • Bare python = Store stub — Runs silently/fails. Fix it by invoking anaconda by its full path.
  • Test-NetConnection false-negative — Reports a port as closed when it is open. Use ping.exe/ssh.exe directly.
  • Testing the wrong destination — After changing config, curl against loopback returns an error and it looks broken, while the actual vhost listens on the public IP. Test through the actual IP.
  • Old behavior after a config change — Stale opcache/cache. Understand revalidate / reload / clear cache.
  • Secrets leaking into logs — Do not echo secrets; mask them or write them to a secret file.
  • Leftover junk files on the server — Use mktemp + trap EXIT to clean up. Do not embed Windows paths in server-side scripts.
  • DA/CustomBuild removes the changes — Use a self-heal script + guard.
  • Overly broad rules — Blocking bots also catches Googlebot and damages SEO. Narrow the scope to specific host+path combinations, whitelist crawlers, and verify every UA.
The biggest danger: one long destructive script

A 50-line heredoc containing rm/systemctl restart/csf -ra can leave the system in a partial state that is hard to recover if something goes wrong halfway through. Send destructive operations one line at a time, check the result, then move to the next line. Before restarting/deleting/changing config, make sure there is actual evidence supporting the action, rather than guessing from symptoms that resemble a problem you have seen before.

11. A real case: working through the whole job safely

A real example: fixing "Googlebot banned by the firewall, damaging SEO" on a shared host.

  • Read-only audit Review csf/lfd/BFM, 5 days of modsec logs, access logs for every domain, robots.txt, and ipset. Write a report without changing anything.
  • Backup A dated folder (csf.*, nginx, iptables/ipset state) + RESTORE.md, chmod 700.
  • Create an 8-step TODO list Unban -> whitelist -> real_ip -> verify -> (last) remove country-block -> memory.
  • Fix one step at a time With a gate (nginx -t) + verification (csf -g, curl) at every step.
  • Build a tool run daily by cron Fetch official bot IPs + auto-unban + log, instead of using a static list.
  • Persist + memory + report verification results at every layer.

The result is that every step is reversible and auditable, and Googlebot/Bing are automatically kept from being banned again.

12. Summary checklist

Prepare the machine (once): Anaconda, invoke python by its full path · Node+npm · PuTTY (plink/pscp) + know the port/hostkey · Git Bash, sshpass · One gitignored secret file · Scan the machine beforehand.

Before requesting any change: Always use a model with 1M context · Research + open and check every link · Create/fix skills first · Start with a read-only audit · Create a .sh/tool, do not fire off ad hoc commands · Backup + RESTORE.md · Validation gate (-t / -l / -n) · Verify after changes + check other sites · Destructive operations one line at a time · "Give me the command" means I will run it myself.

The one principle to remember: research until you are sure, inspect first, back up first, write a script, add a validation gate, then execute, and prove it worked afterward. Speed comes from avoiding time spent recovering systems, not from skipping steps. AI does make the work much faster, but using AI well means always being able to roll back.

I have already written a series covering all the actual work I did with this method, from preparing the VM to completing the domain migration. Have a look.

Feel free to adapt the tools or steps to your own way of working. The principles are the same. If I have left anything out or made a typo, I will come back and correct it later.

Want us to set this up?

Get help with server setup, configuration, migration, or troubleshooting. Save time for the work that matters.

Get a quote

Hit something like this? Stuck somewhere?

Come trade notes on the LinuxThai board

→ Continue on the board

Comments 0

No comments yet — be the first