obsidian/wiki/concepts/shell-static-deploy-patterns.md
2026-04-17 17:32:48 +01:00

3.1 KiB

title aliases tags sources created updated
Shell Deploy Script Patterns for Static Frontends
deploy-script
cp-recursive
frontend-deploy
apache-reload
shell
bash
deploy
apache
nginx
static-files
daily/2026-04-15.md
2026-04-15 2026-04-15

Shell Deploy Script Patterns for Static Frontends

Bash deploy scripts for static frontend projects have two common failure modes: incomplete file copying and serving stale files after deploy. Both are silent — the script exits 0 but the deployment is wrong.

Key Points

  • cp frontend/* glob does not recurse into subdirectories — use cp -r frontend/. "$DEST/" instead
  • cp -r frontend/. (with dot) copies all contents including hidden files and nested dirs; cp -r frontend/ copies the directory itself into the dest
  • Always add mkdir -p "$DEST" before the copy as a safety net
  • After copying static files, reload the web server: systemctl reload apache2 (or nginx -s reload) — otherwise the old cached version is served
  • Distinguish setup.sh (first-time init) from deploy.sh (all subsequent updates) — only deploy.sh runs on updates

Details

The cp Glob Pitfall

# WRONG — skips subdirectories silently
cp frontend/* /var/www/html/

# CORRECT — copies all contents recursively, including hidden files
cp -r frontend/. /var/www/html/

The shell glob frontend/* expands to all non-hidden entries in frontend/ but cp without -r will skip any entries that are directories (it prints a warning but exits 0). Using frontend/. instead of frontend/ as the source avoids creating a nested directory inside the destination.

Apache / Nginx Reload Requirement

Web servers cache configuration and sometimes static file handles. After overwriting files in the web root:

# Apache
systemctl reload apache2

# Nginx
nginx -s reload
# or
systemctl reload nginx

reload is preferred over restart — it re-reads config and gracefully hands off connections without dropping active requests.

#!/bin/bash
set -e

FRONTEND_DIR="/var/www/html/myapp"
BACKEND_DIR="/opt/myapp"

# 1. Copy frontend files
mkdir -p "$FRONTEND_DIR"
cp -r frontend/. "$FRONTEND_DIR/"

# 2. Rebuild and restart backend
cd "$BACKEND_DIR"
docker compose build
docker compose up -d

# 3. Reload web server
systemctl reload apache2

echo "Deploy complete"

setup.sh vs deploy.sh Distinction

  • setup.sh — runs once at first install: creates directories, sets permissions, initializes the database, sets up systemd services
  • deploy.sh — runs on every update: copies code, rebuilds containers, reloads servers

Never run setup.sh after initial setup — it may overwrite config files or re-initialize databases.

Sources

  • daily/2026-04-15.md — Fixed broken deploy scripts in social listening project (commit 7a70283)