I teach web development. Every student gets a personal LAMP hosting space where they publish their work, online, under their own subdomain. That is the heart of the setup: you don't learn the web locally, you learn it by publishing. The catch is that every September, this setup cost me half a day of clicking.
The problem
A student isn't one account. It's five things to create, in the right order, following the right conventions:
- A folder - filed by class year, with the right permissions
- A subdomain -
firstname.mydomain.be, pointing at that folder - An SSL certificate - because a plain HTTP site in 2026, no thanks
- A MySQL database - with its own user and privileges
- An FTP account - locked to their own folder
My hosting is a standard shared plan with cPanel, the interface you'll find at a good share of hosting providers. It does the job. But it does it with a mouse: every operation means navigating a menu, filling in a form, confirming, going back. Count five to ten minutes per student when nothing goes wrong.
Fifteen students works out to somewhere between an hour and a half and two and a half hours. Twice a year. Repeated in exactly the same way, without mistyping a single character in a database name.
And that's where it really hurts: it isn't long, it's mind-numbing. The kind of task where your attention slips at the seventh student and where, three weeks later, you find out someone's FTP account points at the wrong folder.
What I already had
SSH access. Just about every serious shared host provides it, and I only ever used it for the occasional bit of troubleshooting.
Yet cPanel exposes a command-line API, uapi, reachable from that SSH session. Everything the web interface does with forms, this API does with a command. I vaguely knew that. I had never explored it, because the entry cost (reading the docs, testing, getting it wrong) always outweighed the cost of an evening of clicking.
That is exactly the calculation AI changes.
The exploration
I asked Claude Code to explore the server and tell me what could be driven from SSH. Not to write anything: just look, list the available functions, work out the conventions already in place across my previous class years.
The result, within minutes:
| Operation | Command | Status |
|---|---|---|
| Create a subdomain | SubDomain addsubdomain | OK |
| Create a database | Mysql create_database | OK |
| Create a SQL user | Mysql create_user | OK |
| Create an FTP account | Ftp add_ftp | OK |
| Delete a subdomain | delsubdomain | Unavailable |
Four operations out of five automatable straight away. The fifth, deleting a subdomain, only exists in an older version of the API whose binary is hidden by the shared-hosting isolation layer. It stays manual. No great loss: we create fifteen accounts a year and delete them far less often.
SSL holds out. Automatic renewal isn't enabled on my account, and the plugin that issues certificates only exists in the web interface. When a subdomain is created, the certificate installed is self-signed: the browser shows a warning until you issue the real one. It's the one step I still do by hand, but the interface lets you tick every subdomain at once: one minute for the whole class, against five to ten minutes per student before.
Don't stop at the script
At that point I had enough to write a shell script and move on. That is what I would have done two years ago, and the script would have ended up forgotten in a folder, undocumented, unusable by the following September.
The real unit of reuse today isn't the script. It's the skill: a folder holding both the script and the instructions, written for an AI to follow. The difference is concrete. Next September I won't have to find the script or remember its syntax. I'll write:
Create the accounts for class 3tiweb2028:
martin, leila, kwame, sofia, ...
And the skill handles the rest, including reminding me of the conventions I'll have forgotten.
Writing the skill the way you write tested code
A badly written skill is worse than a script: it creates the illusion that the knowledge is captured when it is actually incomplete. So I applied the counter-intuitive method: watch it fail before writing the solution.
I launched an agent with no documentation at all, told it to change nothing, and asked how it would go about the task. Its gaps became my outline:
- It forgot the FTP account - it hadn't guessed that step, even though it's what lets the student upload their files
- It hesitated over naming conventions - two schemes coexisted across my older class years
- It couldn't tell whether the parent folder had to exist - precisely the case for the year's first class
Once the skill was written, I put it in front of another agent and asked it to be harsh. Verdict: 6.5 out of 10, with a precise list of flaws. No connection command, no loop to handle a list, security documented but missing from the procedure, passwords generated then lost for want of capturing them.
That score saved me more time than any review of my own. I know the subject too well to see what's missing.
The two bugs only real execution revealed
With the skill fixed, I tested it on a throwaway account. Two bugs surfaced, both invisible on the page.
A character range instead of a list
The password generator used this character class:
tr -dc 'A-Za-z0-9!#%*+-=?@'
The first password it produced contained an angle bracket: lKlA=JTo<w1VtfpUiWtJ. But the angle bracket isn't in my list.
Here's why: inside a character class, +-= isn't three characters but a range, running from + to = in the ASCII table. It includes the angle bracket, the slash, the comma. Exactly the characters that break a shell string or a connection URL. The hyphen has to go last to be taken literally.
An optional parameter that isn't
Creating the FTP account returned a clean success. But the login actually created wasn't firstname@mydomain.be: it was firstname@accountname.host.fr, attached to the primary domain.
Without an explicit domain= parameter, cPanel picks the account's primary domain. The trap is nasty: the command succeeds, nothing raises a flag, and the mistake only shows up when the student tries to log in with the credentials you handed them. Worse, deletion then fails with "this account does not exist", since you pass it the expected domain rather than the one actually registered.
What it looks like now
The skill is 232 lines of documentation and 146 lines of script. One command, one list of usernames:
bash provision.sh 3tiweb2027 martin leila kwame
Output per student:
--- martin ---
[1/5] subdomain OK martin.mydomain.be
[2/5] permissions OK 750 nobody
[3/5] database OK account_martin
mysql user OK account_martin
privileges OK
[4/5] ftp OK martin@mydomain.be
[5/5] hardening OK .htaccess in img/ uploads/ files/
And on standard output, one CSV line per student, ready to become the table I hand out: credentials, URL, database, FTP login, unique password. Diagnostics go to standard error, which means you can redirect the CSV into a file without polluting it.
| Before | After | |
|---|---|---|
| Per student | 5 to 10 minutes | a few seconds |
| For 15 students | 1h30 to 2h30 | one command |
| Risk of error | high, and rising with fatigue | none, checked at every step |
| Credentials table | copied by hand | generated |
The limits
Honest automation states what it does not do.
- The SSL certificate stays manual - the plugin that issues it only exists in the web interface, but one minute covers the whole class
- Deleting a subdomain stays manual - the function isn't exposed in the API reachable from shared hosting
- A mid-run failure leaves a partial state - the script stops dead and says so, but doesn't undo what it already created
- The script refuses to overwrite an existing account - that's deliberate, but it means recovering from a failure takes a manual cleanup
- Nothing replaces checking - I always look at the first account created before launching the other fourteen
The takeaway
What strikes me isn't the raw time saved. It's that this automation had been within my reach for years. The API existed, the SSH access existed, the skills existed. What was missing was the willingness to spend an evening reading API documentation to save an evening of clicking. The maths never tipped the right way.
AI invented nothing here. It collapsed the cost of exploration. Reading the docs, testing the commands, discovering that a parameter is mandatory when it looks optional: that groundwork, which used to cost me an evening, now takes a few minutes.
It isn't magic for all that. The skill's first draft scored 6.5 out of 10. The two most annoying bugs only surfaced during real execution, on a real server. I was the one who knew which conventions to follow, what the right database name was, why the permissions had to match those of the earlier class years.
AI is excellent at repetitive, tedious tasks. Not because it does them better than I do, but because it makes automating them worth the effort once and for all.
What I'm taking away
- The skill, not the script. A script without instructions is lost by the next school year
- Watch it fail before writing. The gaps of an undocumented agent make the best outline
- Have your own work graded. A well-argued 6.5 out of 10 beats a flattering review
- Test for real, on something disposable. The costliest bugs aren't visible on the page
- Automate what's tedious, not what's hard. Mind-numbing tasks are where humans slip