Secure Bash scripts: set -euo pipefail, trap and exit codes

Published on 2026-10-26 by frfrand

Avoid deployment scripts that fail silently on Linux

Secure Bash scripts: set -euo pipefail, trap and exit codes

A deploy script installs dependencies, fails on a command, and continues as if nothing happened. The result can be a half-updated website. In Bash it's best to treat errors as part of the design, not as a surprise.

I'll show you where the problem is, how to check it without guessing, and what I'd do in an application that actually has users. You don't need to memorize an entire manual to fix a specific failure.

What secure Bash scripts mean in practice

set -e can make the shell exit on some failures, -u detects undefined variables, and pipefail propagates failures within pipelines. No option replaces checking conditions, if, subshells, and commands that allow expected errors.

A clarification before touching the server: if the project is already in production, save the state you found and check which user and machine you are going to run the commands on. Working as root without need and changing several things at once makes it harder to find the cause of an error.

Useful commands to get started

#!/usr/bin/env bash
set -Eeuo pipefail
trap 'echo "Error en línea $LINENO" >&2' ERR
cd /srv/miapp
python -m pip install -r requirements.txt
python manage.py check
# migraciones y reinicios sólo con plan de recuperación

Paths, unit names, and domains are examples. Adapt them to your service. If a command requires administrator permissions, check what changes and why. Diagnostic queries should come before modifications.

Step by step, with checks

  1. Define what success means for each deployment step.
  2. Add explicit validations before operations on databases or files.
  3. Test simulated failures: dependency that does not install, missing variables, and occupied port.
  4. Record the exit code and do not hide stderr.
  5. Before adding an irreversible migration, design backups and compatibility between versions.

After each step, verify an observable result: a state, a file, an HTTP response, or a log entry. If you find an unexpected message, save it to investigate instead of hiding it with repeated restarts.

Real example: an application that needs maintenance

In a reservation application, a deployment installs packages but fails to build a module. Without controls, systemd restarts a process with incompatible files. A script that stops and reports the failure avoids publishing a mixed state.

At the end, test the full path: enter through the domain, call the API, write a test data, and verify that the information is still there after a controlled restart. A server that "starts" is not always an application that works.

Mistakes worth avoiding

Do not use set -e and assume you have handled all errors. Bash conditional contexts have exceptions; write tests that confirm real behavior.

It is also important that credentials do not end up in terminal history, logs, shared files, or screenshots. For users and services, use minimal permissions and document who can revoke access.

How ArduMaker fits into this scenario

Use ArduMaker's Git deployments with validation steps, logs, and a clear startup command. Automating poorly can propagate the failure to multiple VPSs faster.

ArduMaker can help you manage VPSs, deploy repositories, and work with services, databases, and domains from the same platform. It does not replace what we just explained about Linux: precisely for that reason, it is worth understanding diagnostics and having a way to repeat them.

When it is worth automating

If the task repeats, document the manual procedure and its checks first. Then consider a script or a scheduled task with visible errors, logs, and limited permissions. Do not automate a command that you do not yet know how to recover when it goes wrong.

Frequently asked questions

Can I try it without affecting customers? In many cases, yes: a staging VPS or a separate test service allows you to validate the configuration before changing production.

How do I know it went well? Check logs and a functional operation, not just that the command ended with a zero code.

When do I need more resources? When metrics indicate CPU, RAM, disk, or connection saturation. A configuration failure is rarely fixed by hiring more memory.

References

In one sentence: first understand the error, then change one concrete thing and check its effect. That discipline saves you many hours of support.

  • Bash
  • Tutorial
  • ArduMaker

Keep reading

All articles · VPS plans