Running your deployment scripts as root is a fast track to a security incident. If your CI server gets compromised, the attacker owns the entire machine. You need a dedicated deployment user with just enough permissions to pull code, run migrations, and restart services. You'll also need to configure Systemd Services for Long-Running Processes correctly so your deployment user can restart workers without breaking permissions.
Create the deploy user with a locked password and a dedicated home directory. Don't add it to the wheel group immediately. You want explicit control over what commands this user can execute through privilege escalation. Avoid shortcuts here. Security depends on small, intentional boundaries between system accounts.
sudo useradd -m -s /bin/bash deployer
sudo passwd -l deployerSSH key configuration requires immense attention to detail. Your CI system will connect using an SSH private key. Place the corresponding public key inside the deployer user's authorized_keys file. Restrict what that key can do by prepending options in the authorized_keys file itself. This prevents an attacker from executing arbitrary commands even if they steal the CI private key from your build runner.
command="/usr/local/bin/deploy-wrapper.sh",no-port-forwarding,no-x11-forwarding,no-agent-forwarding,no-pty ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG... deploy-bot@ci-serverThe restriction forces the SSH session to run a specific wrapper script instead of giving an interactive shell. That stops interactive terminal access cold. If someone manages to grab the private key, they still cannot spawn a remote shell or tunnel traffic through your server. It's a simple line that saves your infrastructure.
Configuring Sudoers Without Giving Away the Store
Your deployment pipeline will need to run certain root-level commands. Restarting a web server or clearing a cache directory requires elevated privileges. Don't give the deployer user passwordless root access via ALL=(ALL) ALL. That defeats the purpose of isolation.
Edit the sudoers file using visudo and define exact rules for the deployer user. List the precise binaries they are allowed to execute with sudo. Here's a restrictive sudoers configuration snippet you can drop into a file under /etc/sudoers.d/:
deployer ALL=(ALL) NOPASSWD: /bin/systemctl restart nginx, /usr/bin/supervisorctl restart worker-*This snippet permits the deployer user to restart Nginx and Supervisor workers, but nothing else. If an attacker injects a malicious payload into your deployment script and tries to run useradd or rm -rf on system directories, sudo will reject it. Sudo fails closed by default. That is your friend.
Watch out for how path expansion works in sudo. Always use absolute paths for binaries in your sudoers configuration. If you omit the path, a clever user might manipulate their PATH environment variable to execute an arbitrary binary with root privileges.
Locking Down Shell Access and Environment Variables
Even with restricted sudo rules, an interactive shell gives users too much room to maneuver. If your deployment pipeline doesn't need an interactive shell, change the user's shell to nologin or restrict it via restricted bash. However, CI pipelines often require a standard shell to execute build commands and run test suites before swapping symlinks.
When a standard shell is mandatory, lock down the environment. Modify the /etc/environment file and ensure that your deploy user's home directory permissions are strictly set to 700. Other users on the box shouldn't even be able to read the deployer's configuration files or cached dependencies. Permissions matter.
sudo chmod 700 /home/deployer
sudo chown -R deployer:deployer /home/deployerEnvironment variable leakage is another common pitfall. CI pipelines often pass database credentials and API tokens through SSH environment variables. By default, OpenSSH blocks untrusted environment variables, but you must verify that your sshd_config file doesn't have AcceptEnv configured loosely. Check your /etc/ssh/sshd_config file and ensure you only permit explicit variables if your deployment flow depends on them.
Auditing Authentication and Managing SSH Keys
A secure deployment setup is never truly finished. You need a process for rotating SSH keys when team members leave or CI runners get replaced. Leaving old keys in the authorized_keys file is an open door. Audit those files regularly.
Check your system authentication logs for anomalies. Failed public key attempts or unexpected connection times from strange IP addresses should trigger alerts. Combine these SSH and sudo restrictions with regular log audits. Keep an eye on authentication failures and unexpected privilege escalation attempts. A secure deploy pipeline is quiet, predictable, and heavily constrained.











