How to Set Up a Secure Deploy User for GitHub Actions (And Why You Should Never Use Root)

Avoid root in Laravel CI/CD. Here’s how to create a secure "deploy" user for GitHub Actions, with exact commands and a full checklist.

Published by Rizwan on May 13, 2025

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 the deploy 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?

👉 Book a 1:1 consultation →

Let’s secure your infrastructure before it bites back.

How we reviewed this article:

  • Content Process
My process of publishing articles include:
  1. I get an Idea
  2. Get feedback from fellow developers if they want a detailed post on it
  3. Research existing blog posts to see if there's a well written article on it
  4. Write the post
  5. Get feedback from colleagues, improve and publish!