Automated deployment tools need SSH access to your VPS, but granting them full administrative access is unnecessary risk. This guide covers creating a deployment-specific account restricted to only what it needs.
Why a Dedicated Deployment User
- If the deploy key is ever compromised, the blast radius is limited to what the deploy user can do
- Clear audit trail — deployment actions are distinguishable from human administrative actions
- Easier to revoke deployment access specifically without affecting other access
Step 1 — Create the Deployment User
sudo useradd -m -s /bin/bash deploy-bot
Step 2 — Generate a Dedicated SSH Key Pair for Deployment
On your local machine or CI system (not the VPS):
ssh-keygen -t ed25519 -f deploy_key -C "deployment-only" -N ""
Step 3 — Add the Public Key to the Deployment User
sudo mkdir -p /home/deploy-bot/.ssh
sudo nano /home/deploy-bot/.ssh/authorized_keys
Paste the public key content, then:
sudo chown -R deploy-bot:deploy-bot /home/deploy-bot/.ssh
sudo chmod 700 /home/deploy-bot/.ssh
sudo chmod 600 /home/deploy-bot/.ssh/authorized_keys
Step 4 — Restrict Sudo Access to Only Deployment-Related Commands
sudo visudo -f /etc/sudoers.d/deploy-bot
deploy-bot ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart myapp, /usr/bin/systemctl reload nginx, /usr/local/bin/deploy.sh
This lets the deployment user run only these specific commands with elevated privileges — nothing else.
Step 5 — Restrict File System Access
sudo chown -R deploy-bot:deploy-bot /var/www/myapp
The deployment user only needs write access to the application directory it manages, not the entire filesystem.
Step 6 — Restrict the SSH Key to a Specific Command (Advanced, Stronger Isolation)
In authorized_keys, prefix the key with a command restriction so it can only ever run one specific script, regardless of what command is actually sent:
command="/usr/local/bin/deploy.sh",no-port-forwarding,no-X11-forwarding,no-agent-forwarding ssh-ed25519 AAAA...
This is the strongest form of restriction — even if the private key is compromised, an attacker can only trigger the deployment script, nothing else, regardless of what SSH command they attempt to run.
Step 7 — Test the Restricted Access
ssh -i deploy_key deploy-bot@YOUR_SERVER_IP "whoami"
If using the command restriction from Step 6, this will run deploy.sh regardless of the whoami command sent, confirming the restriction is active.
Step 8 — Disable Interactive Login for the Deployment User (Optional, Extra Hardening)
sudo usermod -s /usr/sbin/nologin deploy-bot
Combined with the command restriction, this ensures the account can only ever execute the deployment script via SSH, never an interactive shell.
Auditing Deployment Activity
sudo grep deploy-bot /var/log/auth.log
Combined with your deployment script's own logging (see How to Deploy Automatically on Git Push), gives a complete picture of when deployments ran and by what mechanism.
Common Errors
"This account is currently not available" when testing interactive login — expected if you set the shell to nologin; verify the actual deployment command still works via the command restriction.
Deployment fails with a permission error — verify the deployment user owns (or has explicit access to) every path the deployment script touches.
Best Practices
- Use a dedicated deployment user, never a personal or shared administrative account
- Restrict both sudo privileges and, ideally, the SSH key itself to only the specific deployment command
- Regularly audit and rotate deployment credentials
Continue Reading
- How to Deploy Automatically on Git Push (Webhook-Based Deployment)
- How to Implement the Principle of Least Privilege on a Linux VPS
- CI/CD Pipeline Security Best Practices
Browse more articles in DevOps & CI/CD.
