3.1 KiB
| title | aliases | tags | sources | created | updated | |||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Shell Deploy Script Patterns for Static Frontends |
|
|
|
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 — usecp -r frontend/. "$DEST/"insteadcp -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(ornginx -s reload) — otherwise the old cached version is served - Distinguish
setup.sh(first-time init) fromdeploy.sh(all subsequent updates) — onlydeploy.shruns 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.
Recommended deploy.sh Structure
#!/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 servicesdeploy.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.
Related Concepts
- wiki/concepts/msal-vanilla-js-pkce — deploy pattern for social-reporting-tool where this was encountered
- wiki/architecture/docker-compose-patterns — container restart patterns that work alongside deploy scripts
Sources
- daily/2026-04-15.md — Fixed broken deploy scripts in social listening project (commit
7a70283)