Pulling Latest Changes to Production: A Practical Guide
Deploying code to production via git pull is simple — until your prod server has diverged from your repo. This guide covers the safe workflow, how to handle prod-only changes, .env file management, and Docker-specific gotchas.
1. The Basic Workflow
# On prod server cd /path/to/app git status # check for local changes first git fetch origin git log HEAD..origin/main --oneline # preview incoming commits git pull origin main
Never skip git status and the preview step. Blind pulls are how outages happen.
2. When Prod Has Local Changes
This happens more than it should — someone edits a config file directly on the server, or a script writes logs into a tracked directory. Handle it like this:
Step 1: Identify what changed
git status git diff
Step 2: Decide the fate of each change
- Accidental / junk change (e.g. temp debug logging) → discard it:
git checkout -- <file> - Legitimate prod-only config (should never have been edited directly) → stash it, pull, then reapply or migrate to proper config management:
git stash git pull origin main git stash pop # resolve conflicts if any - Change that should go back into the repo → commit it on a branch and open a PR instead of leaving it as prod drift.
Golden rule
Prod should never be a place where code changes originate. If you find yourself editing files directly on prod regularly, that's a process gap — fix the pipeline, not the symptom.
3. Handling .env Files
.env files are the #1 cause of "why did pulling break prod" incidents. Best practices:
- Never commit
.envto git. Add it to.gitignorefrom day one. - Keep
.envout of the pull path entirely. Since it's untracked,git pullwon't touch it — but verify withgit statusthat it's not accidentally tracked. - Use a template file (
.env.exampleor.env.template) committed to the repo, listing required keys with placeholder/dummy values. When new keys are added, this template changes — that's your signal to update prod's.envmanually. - Diff-check after every pull:
This flags missing keys before the app crashes on startup.diff <(grep -o '^[A-Z_]*' .env.example) <(grep -o '^[A-Z_]*' .env) - Never overwrite prod's
.envwith one from another environment. Secrets and URLs differ; a copy-paste mistake here is a common cause of data loss or leaked credentials. - Consider a secrets manager (Doppler, Vault, AWS Secrets Manager, etc.) for anything beyond a single-server setup — it removes the manual sync problem entirely.
4. Docker + Python Project Considerations
If your app runs in Docker, a git pull alone does nothing to your running containers — you also need to rebuild/restart.
Standard flow
git pull origin main docker compose build # rebuild image with new code docker compose up -d # recreate containers with new image
Key gotchas
.envand Docker Compose: Compose auto-loads.envfrom the project root for variable substitution indocker-compose.yml. If your app also reads.envat runtime (e.g. viapython-dotenv), make sure both the host.envand the one mounted/copied into the container are in sync — don't maintain two separate copies.- Don't bake
.envinto the image. AvoidCOPY .env .envin your Dockerfile. Instead, pass it viaenv_file:indocker-compose.ymlor--env-fileondocker run. This keeps secrets out of image layers and image history. - Dependency changes: if
requirements.txt/pyproject.tomlchanged, a plain restart won't pick up new packages — you needdocker compose build --no-cache(or at least a normal rebuild) beforeup -d. - Migrations: for Django/Flask/etc. with a database, run migrations after the new image is up but before traffic hits it:
docker compose exec app python manage.py migrate - Zero-downtime concern:
docker compose up -don a single container causes a brief outage. For anything customer-facing, look into rolling restarts, blue-green deploys, or at minimum a health-check-gated restart. - Volume-mounted code in dev vs prod: if your dev setup bind-mounts source code for hot-reload, make sure prod's Compose file does not do this — prod should run code baked into the image, not a live mount, to avoid drift between what's deployed and what's actually running.
5. Recommended Deployment Checklist
[ ] git fetch + review incoming diff
[ ] Check for uncommitted local changes on prod
[ ] Resolve/stash any prod-only changes
[ ] git pull
[ ] Diff .env against .env.example for new required keys
[ ] docker compose build (if deps changed)
[ ] Run migrations if needed
[ ] docker compose up -d
[ ] Check logs / health endpoint
TL;DR
- Treat prod as read-only for code — never edit tracked files there.
.envstays untracked, synced manually or via a secrets manager, and never copied blindly between environments.- With Docker,
git pullis only half the deploy — rebuild, restart, and run migrations as separate explicit steps. - Automate this checklist into a deploy script or CI/CD pipeline as soon as you're doing it more than twice.