{"slug": "how-i-let-ai-agents-work-on-my-production-server-without-breaking-it", "title": "\"How I let AI agents work on my production server without breaking it\"", "summary": "A solo operator running 42 domains and several SaaS platforms from Diakopto, Greece, has published a rulebook for letting AI coding agents work on a live production server without breaking it. The core principle is that agents propose changes in a PROPOSED.md file and a human explicitly approves each step before anything is applied, with bans on agent-run builds, pushes, and Prisma migration commands that could silently deploy or reset production data. The rules were derived from real incidents, including an agent that applied a change while the operator's editor was frozen.", "body_md": "description: \"A solo operator's rulebook: agents propose, a human applies. Real incidents and the rules they produced, from a small town in Greece.\"\n\ntags: ai, devops, productivity, webdev\n\ncover_image: \n\n[Image description]\n\nI'm not a classic programmer. I started as a photographer and art director, and today I run a network of 42 domains, a handful of SaaS platforms and a fleet of AI chat assistants from Diakopto, a small town in Greece. Most of that code was written by AI agents working inside my editor over SSH, on the same server that serves real users.\n\nThe agents are faster than me at almost everything. That's exactly the problem. A fast agent with shell access can take down in thirty seconds what took a month to build.\n\nSo I don't try to make the agents smarter. I make the path to production narrower. This post is the rulebook, and most of the rules exist because something went wrong once.\n\nEvery change to production starts as a file, not a command. The agent audits first (read-only: configs, logs, row counts, what's actually deployed) and then writes a `PROPOSED.md` with four parts:\n\nI read it, ask questions, and only then say **GO**. Silence is not a GO. \"Looks fine\" in the middle of a different conversation is not a GO.\n\n**The incident behind the rule:** once, my editor froze while I was still reading a proposal. The agent didn't wait. It applied the change, which happened to pass every check. Nothing broke, and that's what scared me: next time the change might not be one that passes. Since then, the agent's instructions end every proposal with *write it, report, stop*.\n\nAgents write files to disk. Unless I give an explicit GO for that specific step, they don't:\n\n`npm run build`\n`pm2 restart`)` git push`\nThe build one surprises people. On my server, several frontends are served straight from their `dist/` folder. That means **building is deploying**. An agent that \"just runs a quick build to check for errors\" has shipped whatever was on disk to every visitor.\n\nTwo of my repos also had GitHub Actions that deployed on every push to `main`. I switched both off. If a push can deploy, then anything that can push can deploy.\n\n`prisma migrate dev` on the server\n`migrate dev` is a development tool. When it detects drift between your migration history and the real database, its answer can be to reset the database. On a laptop that's a shrug. On production it's a very bad afternoon.\n\nMy database also has partial unique indexes that I created with raw SQL, because Prisma's schema doesn't express them the way I needed. Prisma doesn't know they exist, so any tool that \"syncs\" the schema is a tool that can quietly drop them. For the same reason, `prisma db push` is banned too: it bypasses migration history completely.\n\nSchema changes on the server follow one fixed sequence:\n\n```\npg_dump -Fc mydb > /var/backups/mydb-before-change.dump\npsql mydb -f 2026-09-10-add-column.sql\nnpx prisma generate\n```\n\nSnapshot at the provider, dump, handwritten SQL, update `schema.prisma` to match, regenerate the client, and then a restart that I approve. It's boring. Boring is the point.\n\nI work with three places that can run commands: the terminal on my Mac, a terminal on the server, and the agent itself. Every command I receive is labeled with where it runs, and I get **one command at a time**.\n\nThis sounds bureaucratic until you paste `scp ~/Downloads/logo.png ...` into the server terminal, or a `sudo` command into your Mac. I've done both. My rule of thumb now: if it starts with `cd ~/Downloads` or `scp`, it belongs on the Mac. If it starts with `cd /var/www` or `sudo`, it belongs on the server.\n\nOne of my platforms caches data per client. I asked for a test that proves client A can never see client B's data. The test passed.\n\nThen we broke the code on purpose: made the cache global, so every client shared it. If the test still passed, it was decoration. It failed, loudly, which is the only reason I trust it now.\n\nWhen an agent writes a security test, make it show you the test failing against broken code before you believe it passes against working code.\n\nMy AI chat gateway was configured with a primary model provider and a fallback. Everything \"worked\". An audit showed that the primary had **never** worked on that gateway: first a dead key, then retired model names. For weeks every conversation had silently gone through the fallback, and nothing anywhere said so.\n\nNow every fallback is counted and visible. When the gateway serves a prompt from its fallback path, a counter goes up and the logs say why. A fallback you can't see is a fault that hasn't been reported yet.\n\nA mockup for my company's homepage once had a lovely \"live console\": a site count slowly drifting upward, a made-up hostname, ping values from `Math.random()`. It looked great. It was also lying to every visitor.\n\nThe rule now covers every screen I own: if a number isn't measured, the UI shows **\"—\"**. No placeholder that looks real, no \"estimated\" metric that nobody estimated.\n\nI was once sure a redesign was already live, because I had a screenshot of it. The agent disagreed, and instead of arguing it proved it with four checks: which docroot nginx actually serves, the hash of the file on disk versus what the edge returned, the MIME types, and which stylesheet loaded last. The design had never been live. My screenshot was the prototype, open in another tab.\n\nMemory, mine or the agent's, is not evidence. The server is.\n\n`JOURNAL.md` is append-only and written by the agent. `PENDINGS.md` is the to-do list, and only I edit it.\nIt's slower. A change I could \"just do\" in two minutes takes fifteen: audit, proposal, questions, GO, apply, verify.\n\nWhat it buys is that I can sleep. A recent audit showed four long-running services on the server that had gone more than two weeks without a single restart. That's not because the agents never make mistakes. They make plenty. It's because their mistakes land in a proposal, where they cost me a question instead of an outage.\n\n**How do you work with AI agents near production?** Do you let them run builds or migrations, or do you keep a gate like this? I'd genuinely like to know where other people draw the line.\n\n*Alexandros · Web Host Pro · Diakopto, Greece*", "url": "https://wpnews.pro/news/how-i-let-ai-agents-work-on-my-production-server-without-breaking-it", "canonical_source": "https://dev.to/alexandroskyv/how-i-let-ai-agents-work-on-my-production-server-without-breaking-it-i56", "published_at": "2026-09-10 16:19:45+00:00", "updated_at": "2026-09-10 16:52:41.587671+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "developer-tools", "mlops", "ai-safety"], "entities": ["Prisma", "GitHub Actions", "pm2", "Diakopto"], "alternates": {"html": "https://wpnews.pro/news/how-i-let-ai-agents-work-on-my-production-server-without-breaking-it", "markdown": "https://wpnews.pro/news/how-i-let-ai-agents-work-on-my-production-server-without-breaking-it.md", "text": "https://wpnews.pro/news/how-i-let-ai-agents-work-on-my-production-server-without-breaking-it.txt", "jsonld": "https://wpnews.pro/news/how-i-let-ai-agents-work-on-my-production-server-without-breaking-it.jsonld"}}