Tired of manually uploading files via FTP every time you update your website? In this guide, I’ll show you how to set up automatic deployments from GitHub to your Hostinger server using GitHub Actions and SSH.
Once configured, every time you push to your main branch, your changes will automatically deploy to your live site.
Prerequisites
- A Hostinger hosting account (shared or VPS)
- A GitHub account
- A project you want to deploy (WordPress theme, static site, etc.)
- Terminal access on your local machine (Mac/Linux) or Git Bash (Windows)
Step 1: Generate an SSH Key Pair
First, create an SSH key pair on your local machine. This will allow GitHub Actions to securely connect to your Hostinger server.
Open your terminal and run:
ssh-keygen -t ed25519 -C "your-project-deploy" -f ~/.ssh/your_project_deploy
Replace your-project-deploy with a descriptive name for your project.
When prompted for a passphrase, press Enter twice to leave it empty. Automated deployments can’t enter passphrases.
This creates two files:
~/.ssh/your_project_deploy– Your private key (goes to GitHub)~/.ssh/your_project_deploy.pub– Your public key (goes to Hostinger)
Step 2: Enable SSH Access in Hostinger
- Log into your Hostinger hPanel
- Navigate to Advanced → SSH Access
- Click Enable if SSH isn’t already enabled
- Note down your:
- SSH Host/IP (e.g.,
123.456.789.012) - SSH Username (e.g.,
u847362951) - SSH Port (usually
65002for shared hosting)
- SSH Host/IP (e.g.,
Step 3: Add Your Public Key to Hostinger
- In hPanel, go to Advanced → SSH Access → SSH Keys
- Click Add SSH Key
- Get your public key by running:
cat ~/.ssh/your_project_deploy.pub - Copy the entire output (starts with
ssh-ed25519) - Paste it into Hostinger and give it a name (e.g.,
github-deploy) - Save
Step 4: Find Your Deployment Path
You need the full server path to where your files should be deployed.
For a WordPress theme, it’s typically:
/home/YOUR_USERNAME/domains/yourdomain.com/public_html/wp-content/themes/your-theme
For a regular website:
/home/YOUR_USERNAME/domains/yourdomain.com/public_html
You can verify this by:
- Going to hPanel → Files → File Manager
- Navigating to your target folder
- The path shown (minus the
/files/prefix) combined with/home/YOUR_USERNAME/is your full path
Step 5: Create the GitHub Actions Workflow
In your project, create the directory structure and workflow file:
mkdir -p .github/workflows
Create .github/workflows/deploy.yml:
name: Deploy to Hostinger
on:
push:
branches:
- main
workflow_dispatch: # Allows manual trigger
env:
# Hostinger shared hosting uses port 65002, VPS uses 22
DEFAULT_SSH_PORT: 65002
jobs:
deploy:
name: Deploy to Production
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup SSH key
run: |
mkdir -p ~/.ssh
echo "${{ secrets.SSH_KEY }}" > ~/.ssh/deploy_key
chmod 600 ~/.ssh/deploy_key
ssh-keyscan -p ${{ secrets.SSH_PORT || env.DEFAULT_SSH_PORT }} ${{ secrets.SSH_HOST }} >> ~/.ssh/known_hosts 2>/dev/null || true
- name: Deploy files via rsync
run: |
rsync -avz --delete \
--exclude='.git' \
--exclude='.github' \
--exclude='node_modules' \
--exclude='.env' \
--exclude='README.md' \
-e "ssh -i ~/.ssh/deploy_key -p ${{ secrets.SSH_PORT || env.DEFAULT_SSH_PORT }} -o StrictHostKeyChecking=no" \
./ \
${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}:${{ secrets.DEPLOY_PATH }}/
- name: Cleanup SSH key
if: always()
run: rm -f ~/.ssh/deploy_key
- name: Deployment Summary
run: |
echo "## Deployment Complete" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Commit:** \`${{ github.sha }}\`" >> $GITHUB_STEP_SUMMARY
echo "**Deployed at:** $(date -u)" >> $GITHUB_STEP_SUMMARY
Understanding the Workflow
- rsync: Efficiently syncs only changed files (faster than copying everything)
- –delete: Removes files on the server that don’t exist locally (keeps server in sync)
- –exclude: Skips files/folders you don’t want deployed (like
.git,node_modules) - workflow_dispatch: Lets you manually trigger deployments from GitHub’s UI
Customizing Exclusions
Modify the --exclude lines based on your project. Common exclusions:
--exclude='.git' \
--exclude='.github' \
--exclude='node_modules' \
--exclude='.env' \
--exclude='.env.local' \
--exclude='README.md' \
--exclude='*.md' \
--exclude='tests' \
--exclude='__tests__' \
--exclude='.vscode'
Step 6: Add Secrets to GitHub
- Go to your GitHub repository
- Navigate to Settings → Secrets and variables → Actions
- Click New repository secret for each:
| Secret Name | Value |
|---|---|
SSH_KEY |
Contents of your private key (cat ~/.ssh/your_project_deploy) |
SSH_HOST |
Your Hostinger server IP |
SSH_USER |
Your SSH username (e.g., u847362951) |
SSH_PORT |
65002 (for shared hosting) or 22 (for VPS) |
DEPLOY_PATH |
Full path to deployment folder |
Getting Your Private Key
cat ~/.ssh/your_project_deploy
Copy everything including:
-----BEGIN OPENSSH PRIVATE KEY-----
...
-----END OPENSSH PRIVATE KEY-----
Step 7: Initialize Git and Push
If your project isn’t already a Git repository:
cd your-project
git init
git add .
git commit -m "Initial commit"
git branch -M main
git remote add origin git@github.com:YOUR_USERNAME/your-repo.git
git push -u origin main
Or create the repo with GitHub CLI:
cd your-project
git init
git add .
git commit -m "Initial commit"
gh repo create your-repo-name --private --source=. --push
Step 8: Test the Deployment
Option 1: Push a change
Make any small change, commit, and push:
echo "<!-- deployed -->" >> index.html
git add .
git commit -m "Test deployment"
git push
Option 2: Manual trigger
gh workflow run deploy.yml
Or go to your repo on GitHub → Actions → Deploy to Hostinger → Run workflow
Monitoring the Deployment
Watch the deployment in real-time:
gh run watch
Or check the Actions tab on GitHub.
Troubleshooting
“Permission denied (publickey)”
- Verify your public key is added correctly in Hostinger
- Check that the private key in GitHub secrets is complete (including BEGIN/END lines)
- Ensure there are no extra spaces or newlines in your secrets
“Connection refused” or timeout
- Confirm you’re using the correct port (
65002for shared,22for VPS) - Verify SSH is enabled in hPanel
- Check if your server IP is correct
“rsync: command not found”
This shouldn’t happen on ubuntu-latest, but if it does, add this step before the deploy:
- name: Install rsync
run: sudo apt-get install -y rsync
Files not appearing on server
- Verify your
DEPLOY_PATHis correct - Check the workflow logs for any rsync errors
- Ensure the SSH user has write permissions to the target directory
Deploying Multiple Environments
Want staging and production? Create two workflows:
.github/workflows/deploy-staging.yml – triggers on staging branch
.github/workflows/deploy-production.yml – triggers on main branch
Use different secrets for each:
STAGING_SSH_HOST,STAGING_DEPLOY_PATH, etc.PROD_SSH_HOST,PROD_DEPLOY_PATH, etc.
Security Best Practices
- Never commit secrets – Use GitHub Secrets, never hardcode credentials
- Use deploy keys – Create a dedicated SSH key just for deployments
- Limit access – The SSH key only needs access to the deployment folder
- Private repos – Keep your repo private if it contains any sensitive configuration
- Review exclusions – Never deploy
.envfiles, credentials, or API keys
Conclusion
You now have a fully automated deployment pipeline from GitHub to Hostinger. Every push to your main branch will automatically sync your files to your live server within seconds.
This setup works great for:
- WordPress themes and plugins
- Static websites
- PHP applications
- Any file-based deployments
No more manual FTP uploads. Just push your code and let GitHub Actions handle the rest.