Push code updates to your own server. SSH in, pull, restart the service. Or wrap it in a one-line deploy script. Time: 10–30 seconds for a typical app.
SSH to the server:
ssh deploy@your-server-ip
Go to your project directory and pull:
cd /var/www/myapp
git pull origin main
Install new dependencies (only if package.json / requirements.txt changed):
npm install --production
# or: pip install -r requirements.txt
# or: bundle install --deployment
Build (if needed):
npm run build
Restart the service:
# If using systemd:
sudo systemctl restart myapp
# If using pm2:
pm2 reload myapp
# If using Docker:
docker compose pull && docker compose up -d
That's it — the new code is live.
You'll do this often enough that a script saves real time. deploy.sh on the server:
#!/usr/bin/env bash
set -euo pipefail
cd /var/www/myapp
git pull origin main
npm install --production
npm run build
sudo systemctl restart myapp
echo "✓ Deployed at $(date)"
Make it executable: chmod +x deploy.sh. Now redeploy is one command (after SSHing in):
./deploy.sh
Don't even SSH manually — script it from your Mac:
# deploy.sh on your laptop
#!/usr/bin/env bash
ssh deploy@your-server-ip '/var/www/myapp/deploy.sh'
Or directly inline:
ssh deploy@your-server-ip 'cd /var/www/myapp && git pull && npm install --production && npm run build && sudo systemctl restart myapp'
sudo systemctl restart myapp prompts for a password, your script breaks. Add an /etc/sudoers.d/myapp-restart entry allowing your deploy user to run that one command without a password.
The naive restart drops in-flight requests. For real production:
Type=notify — your service signals "ready" before systemd kills the old one. Smaller downtime window than a default restart.up -d — for Compose-managed services, Docker handles rolling replacement of one container at a time if you set deploy.update_config.For an automatic deploy on every push to main, the cheapest patterns:
workflow_dispatch or on: push: main → action runs ssh ... 'deploy.sh'. Needs an SSH deploy key stored as a GitHub Secret.deploy.sh. Beware: public webhook endpoints need signature verification (similar to Stripe webhooks).If the new deploy is broken:
cd /var/www/myapp
git reset --hard HEAD~1 # back to the previous commit
npm install --production
npm run build
sudo systemctl restart myapp
This rolls forward to the previous commit. Better long-term: keep last N releases in numbered directories and symlink current at one — that gives instant rollback without rebuilding.