Still deploying as root? One mistake and you’ve lost the entire server.
Laravel developers often take shortcuts in CI/CD, especially when setting up GitHub Actions. SSH into the server as root, run a few Artisan commands, done. But that convenience comes at a dangerous cost.
This guide shows you how to create a dedicated deploy
user on your server, restrict its access, and use it safely with GitHub Actions.
By the end, you’ll have a production-ready deployment pipeline that’s far more secure and just as fast.
Why not root?
Using root in your deploy workflow is like leaving your front door wide open with a “Welcome, hackers!” sign.
Here’s what’s wrong with it:
- Total access = total risk. A misfire in your workflow could delete system files, kill services, or expose user data.
- Key leaks = full control. If your GitHub Actions private key is ever compromised, an attacker gets root shell.
- You don’t need it. Your deployment doesn’t require installing packages or modifying users. It just needs to push code and run Artisan commands.
- Bad logs and blame. Every change is logged as root. That kills traceability.
So what’s better? A purpose-built, limited-access user.
What we’ll do
You’ll create a deploy
user on your server, give it just enough SSH access, limit permissions to your Laravel app folder, and use it in your GitHub Actions pipeline.
The process includes:
- Setting up the user and SSH
- Restricting sudo privileges (if needed)
- Updating your deployment workflow to use this new user
- Hardening access and keys
Let’s get started.
Step-by-step commands
Run these as root once, to set things up.
1. Create the deploy user
adduser --disabled-password --gecos "" deploy
This creates a deploy
user with no password login. We’ll use SSH keys only.
2. Add SSH access
Create the .ssh
directory and add your GitHub Actions public key:
mkdir -p /home/deploy/.ssh
nano /home/deploy/.ssh/authorized_keys
Paste your public key.
Then secure the folder:
chown -R deploy:deploy /home/deploy/.ssh
chmod 700 /home/deploy/.ssh
chmod 600 /home/deploy/.ssh/authorized_keys
3. Set home directory permissions
chown -R deploy:deploy /home/deploy
chmod 755 /home/deploy
4. Grant access to your app directory
Assuming your Laravel app is in /var/www/my-app
:
chown -R deploy:www-data /var/www/my-app
chmod -R 775 /var/www/my-app
You may also want to restrict deploy
from writing outside /var/www/my-app
by default. To be extra strict, chroot or RBAC controls can help. But they’re optional at this stage.
5. Allow controlled sudo (optional)
Let’s say you want deploy
to restart services, like PHP-FPM. Add safe sudo rules.
Edit with:
visudo
Add:
deploy ALL=(ALL) NOPASSWD: /bin/systemctl restart php8.3-fpm.service
Replace php8.3-fpm
with your actual service.
This allows the deploy
user to run just that command with sudo. No full root access.
How to use in GitHub Actions
Here’s how to update your workflow to use the new user:
# .github/workflows/deploy.yml
name: Deploy to Production
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: SSH Deploy
uses: appleboy/[email protected]
with:
host: ${{ secrets.DEPLOY_HOST }}
username: deploy
key: ${{ secrets.DEPLOY_SSH_KEY }}
script: |
cd /var/www/my-app
git pull origin main
composer install --no-dev --optimize-autoloader
php artisan migrate --force
php artisan config:cache
php artisan view:cache
sudo systemctl restart php8.3-fpm
Notes:
- Your private SSH key must be added to GitHub Secrets (never committed).
- The public key should match what’s in
authorized_keys
for thedeploy
user. - If your deployment uses
rsync
, adjust permissions accordingly.
Full security checklist
Before you call it done, go through this list:
✅ You’re no longer using root
in CI/CD
✅ deploy
user exists with SSH key access only
✅ Only the app directory is writable
✅ Password login is disabled for all users (/etc/ssh/sshd_config
)
✅ The SSH key in GitHub is read-only
✅ deploy
can only run specific sudo
commands
✅ No secrets (SSH, .env, tokens) are committed to git
✅ Your firewall limits port 22 to trusted IPs
✅ You’ve documented key rotation and user access policies
✅ Logs are stored and rotated for SSH and sudo usage
This setup alone reduces 90% of the risks most small Laravel teams face when deploying via GitHub Actions.
Conclusion
Using root for deployments is fast until it’s fatal.
There’s no reason your Laravel CI/CD pipeline should have the keys to the kingdom. A well-configured deploy
user gives you all the automation with none of the risk.
You’ll prevent accidental damage, stay audit-friendly, and reduce attack surface instantly.
Need help setting this up the right way? Or want a full review of your Laravel deployment pipeline?
Let’s secure your infrastructure before it bites back.
How we reviewed this article:
- Content Process
- I get an Idea
- Get feedback from fellow developers if they want a detailed post on it
- Research existing blog posts to see if there's a well written article on it
- Write the post
- Get feedback from colleagues, improve and publish!