AI did the Install!
That was it. One prompt. From there, Codex CLI handled the entire deployment process automatically.
Installing a full blog platform used to be a multi-step process that required time, patience, and a fair amount of manual configuration. This time, I decided to try something different: using ChatGPT Codex CLI to deploy Ghost on my server.
Instead of logging in and installing everything step by step, I issued a single prompt:
"I will set the DNS record, but I would like you to configure the server to host a blog engine at the subdomain blog.jaenulton.com and store files for that subdomain at /var/www/blogjaenulton. Then I would like you to fetch and install Ghost from Github and I think it require node.js running and a MySQL database. Do what it takes to get it done please."
That was it. One prompt.
From there, Codex CLI handled the entire deployment process automatically. It installed Node.js, configured MySQL, created the database, set up the proper directories, fetched Ghost from GitHub, and prepared the server to run the blog on the specified subdomain. Permissions, dependencies, and configuration were all handled without any intervention.
The entire installation took about fifteen minutes.

What impressed me most was not just the speed, but the autonomy. There was no constant back-and-forth or manual troubleshooting. The prompt was clear, and Codex executed it like a seasoned systems administrator.
For anyone managing servers or deploying applications, this kind of workflow feels like a genuine shift. Instead of manually performing every step, you can simply describe the outcome you want and let the system build it for you.
Fifteen minutes later, my Ghost blog was live and ready — all from a single well-designed prompt. The list of commands the AI issued are below as well as the reason for each and every command.
- whoami; hostname; pwd
Reason: confirm local user/machine and current workspace. - Get-CimInstance Win32_OperatingSystem | Select-Object Caption,Version,OSArchitecture | Format-List
Reason: verify OS type/version.
Reason: check if web/db/runtime binaries existed locally. - Get-ChildItem -Force
Reason: inspect local workspace contents. - wsl -l -v
Reason: check if local Linux subsystem existed.
Reason: verify SSH client availability. - Get-ChildItem -Force $HOME.ssh -ErrorAction SilentlyContinue
Reason: find SSH keys/known hosts. - Get-Content $HOME.ssh\config -ErrorAction SilentlyContinue
Reason: check saved SSH host aliases. - rg --files
Reason: search workspace files for server target info. - Get-Content $HOME.ssh\known_hosts | Select-Object -First 20
Reason: extract likely server IP/host entries. - ssh -o BatchMode=yes ... root@homedash.jaenulton.com "echo connected && uname -a"
Reason: test root SSH access to likely server. - ssh -o BatchMode=yes ... jaenu@homedash.jaenulton.com "echo connected && uname -a"
Reason: test non-root account availability. - ssh -o BatchMode=yes ... root@72.60.228.132 "echo connected && uname -a"
Reason: verify direct IP access. - ssh root@72.60.228.132 "set -e; ... os-release ... services ... bins ... ss -tulpn ..."
Reason: baseline audit of remote OS/services/ports. - ssh root@72.60.228.132 "ls -la /etc/nginx/sites-available; ... sites-enabled"
Reason: inspect Nginx site layout. - ssh root@72.60.228.132 "grep -R --line-number "server_name" ..."
Reason: discover existing server_name usage (first attempt had quote error). - ssh root@72.60.228.132 "ls -la /etc/letsencrypt/live ..."
Reason: check existing certificates. - ssh root@72.60.228.132 'grep -R --line-number server_name ...'
Reason: rerun server_name scan with fixed quoting. - ssh root@72.60.228.132 "node -v; npm -v; yarn -v || true; git --version"
- ssh root@72.60.228.132 "apt-get update ...; apt-get install ...; npm install -g ghost-cli ...; ghost --version;
ghost help install ..."
Reason: install/verify Ghost CLI dependencies. - ssh root@72.60.228.132 "ghost install --help ..."
Reason: confirm install flags. - ssh root@72.60.228.132 "ghost setup --help ..."
Reason: confirm setup-stage options. - ssh root@72.60.228.132 'set -euo pipefail ... ghost install ...' (multi-line)
Reason: initial combined DB+install script attempt (failed due PowerShell parsing). - @'...script...'@ | ssh root@72.60.228.132 "bash -s"
Reason: rerun combined script via stdin (progressed but failed at archive selection). - ssh root@72.60.228.132 "curl -fsSL https://api.github.com/repos/TryGhost/Ghost/releases/latest ..."
Reason: inspect latest GitHub release payload. - @'...zipball_url install script...'@ | ssh root@72.60.228.132 "bash -s"
Reason: try install using GitHub zipball_url archive (failed: root disallowed). - @'...create ghostmgr user script...'@ | ssh root@72.60.228.132 "bash -s"
Reason: create non-root install user (failed due CRLF path issue). - ssh root@72.60.228.132 "id -u ghostmgr ...; useradd ...; sudoers ...; chown ..."
Reason: create ghostmgr and permissions using one-liner. - ssh root@72.60.228.132 "cp -f /root/.ghost_blog... /home/ghostmgr/...; chown ...; chmod ..."
Reason: give install user temporary DB password access. - ssh root@72.60.228.132 "sudo -u ghostmgr bash -lc '... ghost install ...'"
Reason: install as non-root (failed due quote terminator issue). - @'...write /tmp/ghost_install_blog.sh ...'@ | ssh root@72.60.228.132 "bash -s"
Reason: attempt script-file approach (failed due CRLF in script path). - $script = @'...'; ($script -replace "\r","") | ssh root@72.60.228.132 "sudo -u ghostmgr bash -s"`
Reason: run clean-LF script as ghostmgr (failed: unsupported archive format). - ssh root@72.60.228.132 "curl -I -L ... https://github.com/TryGhost/Ghost/releases/download/v6.17.0/Ghost-
6.17.0.zip ..."
Reason: verify if direct GitHub release asset existed (404). - $script = @'... git clone + ghost install ...'@ | ssh ...
Reason: attempt with cloned GitHub source and install (failed with syntax EOF).
Reason: clone Ghost GitHub repo successfully. - ssh root@72.60.228.132 "cat /home/ghostmgr/.ghost_blogjaenulton_db_pass"
Reason: fetch DB password value for command-line install attempt. - ssh root@72.60.228.132 "sudo -u ghostmgr bash -lc 'cd /var/www/blogjaenulton && ghost install ...'"
Reason: install Ghost (failed because directory was not empty). - ssh root@72.60.228.132 "mv /var/www/blogjaenulton/ghost-github-source ...; chown ..."
Reason: empty install target directory per Ghost CLI requirement. - ssh root@72.60.228.132 "sudo -u ghostmgr bash -lc 'cd /var/www/blogjaenulton && ghost install ...'"
Reason: successful Ghost production install. - ssh root@72.60.228.132 "systemctl is-active ghost...; is-enabled ...; is-active nginx; is-active mariadb"
Reason: verify service states. - ssh root@72.60.228.132 "sudo -u ghostmgr bash -lc 'cd /var/www/blogjaenulton && ghost ls'"
Reason: confirm Ghost instance/URL/port. - ssh root@72.60.228.132 "cat /etc/nginx/sites-available/blog.jaenulton.com.conf"
Reason: inspect generated Nginx vhost. - ssh root@72.60.228.132 "curl -sS -I -H 'Host: blog.jaenulton.com' http://127.0.0.1 ..."
Reason: confirm DNS resolution to server IP. - ssh root@72.60.228.132 "command -v certbot ...; certbot --version ..."
Reason: confirm SSL tooling availability. - ssh root@72.60.228.132 "grep -R --line-number '"email"' /etc/letsencrypt/accounts ..."
Reason: check existing certbot account/email metadata. - ssh root@72.60.228.132 "ls -1 /etc/letsencrypt/live | grep -E 'jaenulton|blog' ..."
Reason: list existing certs/domains. - ssh root@72.60.228.132 "certbot --nginx -d blog.jaenulton.com --non-interactive --agree-tos --redirect"
Reason: issue cert and enforce HTTP->HTTPS redirect. - ssh root@72.60.228.132 "sudo -u ghostmgr bash -lc 'cd /var/www/blogjaenulton && ghost config url https://
blog.jaenulton.com && ghost restart'"
Reason: update canonical URL to HTTPS and restart. - ssh root@72.60.228.132 "nginx -t"
Reason: final Nginx syntax validation. - ssh root@72.60.228.132 "curl -sS -I https://blog.jaenulton.com | head -n 8"
Reason: verify HTTPS responds 200. - ssh root@72.60.228.132 "mysql -Nse "SHOW DATABASES LIKE ..."; mysql -Nse "SELECT ...""
Reason: DB/user validation attempt (failed quoting). - ssh root@72.60.228.132 "systemctl status ghost...; ...; ls -ld /var/www/blogjaenulton ..."
Reason: confirm Ghost runtime and filesystem ownership. - ssh root@72.60.228.132 "rm -f /home/ghostmgr/.ghost_blog...; ls -l /root/.ghost_blog..."
Reason: cleanup temp credential copy; keep root-only secret file.