Manually uploading files via FTP every time you make a change? That's not just tedious — it's risky. One wrong file, one forgotten upload, and your site breaks. Automated deployment eliminates these problems entirely. Push your code to Git, and your site updates itself.
Here's how to set it up, from simple Git hooks to full CI/CD pipelines.
Why Automate Deployments?
Manual deployment has real costs:
- Human error: Forgetting to upload a file or uploading the wrong version
- Time waste: FTP transfers, SSH sessions, running build commands by hand
- No rollback: When something breaks, reverting is painful
- Inconsistency: "It works on my machine" becomes a daily headache
Automated deployment solves all of these. Your deployment process becomes reproducible, fast, and reversible.
Method 1: Git Hooks (Simplest)
If your site runs on a VPS, Git hooks are the easiest way to deploy automatically.
How It Works
- You push code to a bare Git repository on your server
- A
post-receivehook runs automatically - The hook copies your code to the web directory
Setup
On your server, create a bare repository:
mkdir -p /home/deploy/mysite.git
cd /home/deploy/mysite.git
git init --bare
Create the post-receive hook:
nano hooks/post-receive
Add this script:
#!/bin/bash
TARGET="/var/www/mysite"
GIT_DIR="/home/deploy/mysite.git"
BRANCH="main"
while read oldrev newrev ref
do
if [ "$ref" = "refs/heads/$BRANCH" ]; then
echo "Deploying $BRANCH to $TARGET..."
git --work-tree=$TARGET --git-dir=$GIT_DIR checkout -f $BRANCH
# Run build steps if needed
cd $TARGET
npm install --production 2>&1
npm run build 2>&1
# Restart application
pm2 restart mysite 2>&1
echo "Deployment complete!"
fi
done
Make it executable:
chmod +x hooks/post-receive
On your local machine, add the server as a remote:
git remote add production deploy@yourserver:/home/deploy/mysite.git
Now deploy with:
git push production main
That's it. Push and your site updates.
Pros and Cons
Pros: Simple, no third-party services, instant deployment
Cons: No build environment isolation, limited error handling, single server only
Method 2: GitHub Actions (Recommended)
GitHub Actions gives you a proper CI/CD pipeline — build, test, and deploy automatically when you push to GitHub.
Basic Deployment Workflow
Create .github/workflows/deploy.yml in your repository:
name: Deploy to Production
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Build
run: npm run build
- name: Deploy to server
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /var/www/mysite
git pull origin main
npm install --production
npm run build
pm2 restart mysite
Setting Up Secrets
In your GitHub repository:
- Go to Settings → Secrets and variables → Actions
- Add these secrets:
SERVER_HOST: Your server IPSERVER_USER: SSH usernameSSH_PRIVATE_KEY: Your private SSH key
WordPress Deployment
For WordPress sites, deploy theme or plugin changes:
name: Deploy WordPress Theme
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy via rsync
uses: burnett01/rsync-deployments@6.0.0
with:
switches: -avzr --delete --exclude='.git'
path: wp-content/themes/mytheme/
remote_path: /var/www/html/wp-content/themes/mytheme/
remote_host: ${{ secrets.SERVER_HOST }}
remote_user: ${{ secrets.SERVER_USER }}
remote_key: ${{ secrets.SSH_PRIVATE_KEY }}
Pros and Cons
Pros: Free for public repos (2,000 minutes/month for private), runs tests before deploying, great ecosystem of actions
Cons: Requires GitHub, slight delay (1-3 minutes for pipeline to run)
Method 3: Simple Deploy Script
Sometimes you don't need a full pipeline — just a script that deploys reliably.
deploy.sh
#!/bin/bash
set -e # Exit on any error
SERVER="deploy@yourserver.com"
REMOTE_DIR="/var/www/mysite"
BRANCH="main"
echo "🚀 Starting deployment..."
# Ensure we're on the right branch
CURRENT_BRANCH=$(git branch --show-current)
if [ "$CURRENT_BRANCH" != "$BRANCH" ]; then
echo "❌ Not on $BRANCH branch. Aborting."
exit 1
fi
# Check for uncommitted changes
if [ -n "$(git status --porcelain)" ]; then
echo "❌ Uncommitted changes found. Commit first."
exit 1
fi
# Build locally
echo "📦 Building..."
npm ci
npm run build
# Deploy
echo "📤 Deploying to server..."
rsync -avz --delete \
--exclude='.git' \
--exclude='node_modules' \
--exclude='.env' \
./dist/ $SERVER:$REMOTE_DIR/
# Restart remote application
echo "🔄 Restarting application..."
ssh $SERVER "cd $REMOTE_DIR && pm2 restart mysite"
echo "✅ Deployment complete!"
Run it:
chmod +x deploy.sh
./deploy.sh
Zero-Downtime Deployments
For production sites where every second matters, deploy without taking the site offline:
#!/bin/bash
DEPLOY_DIR="/var/www/mysite"
RELEASE_DIR="/var/www/releases/$(date +%Y%m%d%H%M%S)"
# Deploy to a new release directory
mkdir -p $RELEASE_DIR
git --work-tree=$RELEASE_DIR checkout -f main
# Install and build
cd $RELEASE_DIR
npm install --production
npm run build
# Swap the symlink (atomic operation)
ln -sfn $RELEASE_DIR $DEPLOY_DIR
# Restart app
pm2 restart mysite
# Keep only last 5 releases
ls -dt /var/www/releases/*/ | tail -n +6 | xargs rm -rf
The symlink swap is atomic — visitors never see a half-deployed state.
Rollback Strategy
Every deployment system needs a way to undo mistakes. If using the release directory approach above:
# List available releases
ls -lt /var/www/releases/
# Rollback to previous release
ln -sfn /var/www/releases/20260228143022 /var/www/mysite
pm2 restart mysite
With Git:
# Revert to previous commit
git revert HEAD
git push origin main
# Automated pipeline redeploys the reverted code
Best Practices
Never deploy directly to production without testing. Even a simple
npm testcatches obvious issues.Use environment variables for configuration. Never commit secrets, API keys, or database passwords.
Deploy frequently in small increments. Small changes are easier to debug when something breaks.
Monitor after deployment. Watch your error logs and uptime monitoring for the first 15 minutes after each deploy.
Keep deployment fast. If your pipeline takes more than 5 minutes, optimize it. Slow deployments discourage frequent releases.
Document your process. Write down how to deploy manually as a fallback. Automation fails sometimes.
Choose Your Approach
Just starting out?
├── Static site → Git hooks or rsync script
├── WordPress → rsync with GitHub Actions
├── Node.js app → GitHub Actions with PM2
└── Complex app → Full CI/CD pipeline
Growing team?
├── GitHub Actions (most teams)
├── GitLab CI (if using GitLab)
└── Jenkins (enterprise, self-hosted)
Deploy with Confidence
Automated deployment removes the stress and risk from putting your code live. Start simple — even a basic deploy script is better than manual FTP uploads — and evolve your process as your project grows.
At DeployBase, we support Git-based deployments out of the box. Our VPS plans come with SSH access, Git pre-installed, and the flexibility to set up any deployment workflow you prefer. Whether you're using Git hooks, GitHub Actions, or custom scripts, DeployBase gives you the infrastructure to deploy fast and reliably.
Get started with DeployBase → — hosting built for developers who ship.




