You already pay for hosting and a domain. So why are you still opening FileZilla, finding the right folder, dragging files across, and hoping you did not miss one, every single time you change a line of text?
That workflow is slow, and worse, it is unreliable. Files get missed. Old versions get overwritten. Nobody is quite sure what is actually live.
Set this up once and it goes away. You push your code to GitHub, and your live site updates itself. That is the whole promise, and it takes about fifteen minutes to wire up.
- What You Are Actually Building
- Step 1: An FTP Account Scoped to public_html
- Step 2: Three Repository Secrets in GitHub
- Step 3: Clear the Host's Default index.html
- Step 4: The Workflow File
- Step 5: Commit, Push and Watch It Run
- If It Does Not Work
- Security Notes Worth Taking Seriously
- When Cloudflare Pages Is the Better Answer
- The Whole Setup in Five Lines
- Frequently Asked Questions
What You Are Actually Building
Three pieces, and none of them are complicated on their own.
An FTP account
Created on your hosting and scoped so it can only reach your web root. Not your control panel login.
Three GitHub secrets
That account's server, username and password, stored encrypted in the repository settings.
A workflow file
One YAML file in your repo that logs in over FTP and syncs your files on every push to main.
After that, a single push is your deploy button.
This works for any static site: plain HTML, or the built output of Astro, Hugo, Eleventy, Jekyll and similar. It is not the right tool for a WordPress site, for a reason worth knowing before you try it, and there is a question about that at the bottom.
Step 1: An FTP Account Scoped to public_html
In your hosting panel, go to FTP Management, then Create FTP Account. Set a username and a strong password, then, and this is the part that matters, choose the Custom directory option and browse to public_html. See that at 0:44.
public_html, then even in the worst case it can only touch your website files, not your whole hosting account. Do not use your main panel login for this.
When it is created, note the server path it shows you, something like /home/<account>/public_html/. You will not need to type it anywhere, but it confirms the account is rooted where you think it is, and step 4 depends entirely on that.
Step 2: Three Repository Secrets in GitHub
In your repository, go to Settings, then Secrets and variables, then Actions, then New repository secret. Add three of them.
| Secret name | Value |
|---|---|
FTP_SERVER | ftp.yourdomain.co.ke |
FTP_USERNAME | The FTP account you just created, not your panel login |
FTP_PASSWORD | The password you set when creating that FTP account |
See them being added at 1:58. Secrets are encrypted and masked in the logs, so they never appear in your workflow output. This is the correct place for them. Never put credentials in the workflow file itself, because that file is committed to the repository, and anything committed is in the history for good.
Step 3: Clear the Host's Default index.html
Jump to 2:50. Do this before your first deploy, not after.
Most hosts drop a default index.html or a placeholder page into public_html when the account is created. If yours is still there, it can end up being served instead of your site, and you will spend twenty minutes convinced the deploy failed when it worked perfectly. Clear it out in the file manager first.
Step 4: The Workflow File
In your project, create the folder .github/workflows, and inside it a file called deploy.yml. Here it is in full.
name: Deploy Static HTML to HostAfrica
on:
push:
branches:
- main
jobs:
deploy:
name: ๐ Deploy Static Site
runs-on: ubuntu-latest
steps:
- name: ๐ Get latest code
uses: actions/checkout@v4
- name: ๐ Sync HTML files to HostAfrica
uses: SamKirkland/FTP-Deploy-Action@v4.3.5
with:
server: ${{ secrets.FTP_SERVER }}
username: ${{ secrets.FTP_USERNAME }}
password: ${{ secrets.FTP_PASSWORD }}
server-dir: ./
Two lines to check before you commit it.
The branch name
The file above triggers on main. If your repository still uses master, change it, or nothing will ever run, and nothing will tell you why. See it at 3:56.
server-dir, which is the line people get wrong
It is set to ./ because the FTP account from step 1 is already rooted at public_html. When it logs in, it is standing in the web root, so the destination is "here".
If you set server-dir: public_html/ as well, you get public_html/public_html/ and your site does not appear. If instead you created your FTP account at the hosting account root rather than scoping it, then you do need server-dir: public_html/. Match this one line to how you scoped the account. It accounts for most of the failures people hit with this setup.
Step 5: Commit, Push and Watch It Run
Jump to 4:58. Stage your changes, commit them with a message you will recognise later, and push to the branch named in the workflow.
git add . git commit -m "deploy from github to hosting" git push origin main
Then go to the Actions tab in your repository. The workflow run appears, named after your commit message, showing as In progress. See it at 6:07.
Give it a minute or so. When it completes, refresh your domain and the site is live. The finished result is at 6:42.
From now on that is your entire deployment process. Change a file, commit, push. The site updates itself.
If It Does Not Work
Three things account for most failures, and they are easy to tell apart.
| What you see | Almost certainly | What to do |
|---|---|---|
| The Action succeeded but the site did not change | server-dir | Check whether the FTP account is scoped to public_html or to the account root, and match the line to it. Also check the host's placeholder file is gone |
| The Action failed on login | Wrong server, or the wrong username | Confirm FTP_SERVER is right for your host, and that FTP_USERNAME is the FTP account rather than your control panel username. Those two get mixed up constantly |
| The workflow never ran at all | File path or branch name | The file must be at exactly .github/workflows/deploy.yml, and your branch must be the one named in the branches list |
The Actions log tells you which of these it is. Open the failed run and read it, because the error is usually in plain English rather than a stack trace.
Security Notes Worth Taking Seriously
- Use a dedicated FTP account, scoped to the web root. Never your main hosting login. This is the one that limits the damage if anything else goes wrong.
- Rotate the password if it is ever exposed, including on a screen share, in a video, or in a screenshot pasted into a chat.
- Secrets belong in GitHub Secrets, never in the workflow file and never anywhere else in the repository. A commit is permanent.
- Plain FTP is not encrypted. If your host offers FTPS or SFTP, use it instead. The FTP-Deploy-Action supports secure protocols, and it is worth the extra two minutes to switch.
- Know that it syncs rather than simply uploading. The server is made to match the repository, so a file you take out of the repository goes from the server too. That is the behaviour you want for a static site, and exactly the behaviour you do not want anywhere files appear on the server that were never in your repository.
While you are tightening things up, if the site is not yet on HTTPS then that matters more than the deployment pipeline does. We have written about moving a site from HTTP to HTTPS separately.
When Cloudflare Pages Is the Better Answer
Worth being honest about this, because it is the same job done with less machinery.
Everything above exists because you are deploying to shared hosting, where FTP is the only door in. If you are not tied to a shared host, platforms like Cloudflare Pages, Netlify and Vercel connect to a GitHub repository directly. No FTP account, no secrets to manage, no server-dir to get wrong, and a free tier that covers a static site comfortably. Our guide to hosting a website on Cloudflare for free walks through that route, and there is a wider comparison of the free hosts too.
So use the FTP method when you have a reason to be on shared hosting: a control panel your team already knows, email on the same account, PHP running alongside the static pages, or a host you have already paid for the year. If none of those apply, connect the repository to a platform that speaks Git natively and skip this entire article.
The Whole Setup in Five Lines
Automatic deployment, start to finish
- One FTP account, scoped to
public_html, never your panel login. - Three secrets in GitHub:
FTP_SERVER,FTP_USERNAME,FTP_PASSWORD. - One
deploy.ymlat.github/workflows/deploy.yml, naming the branch you actually use. - The host's placeholder
index.htmlcleared out before the first run. server-dir: ./when the FTP account is already rooted at the web root.
Then never open FileZilla again.
- How to Host a Website on Cloudflare for Free (From GitHub to Live)
- Best Web Hosting for Kenya: HostAfrica, Hostinger, Namecheap & More Compared
- Web Hosting in Kenya: What It Is and Why Your Website Needs It
- Domain Registration and Web Hosting Services in Kenya: The Complete Setup Guide
- How to Build a Website With AI and Host It Free (Full Walkthrough)
Frequently Asked Questions
How do I deploy a website from GitHub to shared hosting?
Create an FTP account on your hosting scoped to the web root, store its server, username and password as three GitHub repository secrets, then add a workflow file at .github/workflows/deploy.yml that runs an FTP deploy action on every push to your main branch. After that, pushing to GitHub updates the live site by itself.
Why did my site not change after the Action ran successfully?
Almost always the server-dir line. If your FTP account is already rooted at public_html and you also set server-dir to public_html, the files land in public_html/public_html and nothing appears on the site. The other common cause is the host's placeholder index.html still sitting in the web root and being served ahead of yours.
What should server-dir be set to?
It depends on how you scoped the FTP account, and it must match. If the account is rooted at public_html, use ./ because the login already lands in the web root. If you created the account at the hosting account root instead, use public_html/. Getting this line wrong is the single most common failure.
Is it safe to put FTP credentials in GitHub?
In GitHub Secrets, yes, with one condition. Secrets are encrypted and masked in the logs, so they do not appear in your workflow output. The condition is that you use a dedicated FTP account scoped to the web root, never your main hosting login, so the worst case is limited to your website files. Never write credentials into the workflow file itself, because that file is committed to the repository.
Does this work for a WordPress site?
Not as described here, and it is worth understanding why. The action syncs rather than simply uploading, so what is on the server is made to match what is in the repository. A WordPress site keeps things on the server that are not in your repository, your entire uploads folder among them. Use this for static sites, and use a plugin-based or managed deployment for WordPress.
What if my branch is called master rather than main?
Change the branch name in the workflow file to match. The on.push.branches list decides what triggers a deploy, so if it says main and your branch is master, the workflow will never run at all, with no error to tell you why.
Does GitHub Actions cost anything for this?
For a small static site the usage is a few seconds per deploy, which sits inside GitHub's free allowance. Public repositories and private repositories are billed differently, so check GitHub's current billing page for the exact figures rather than trusting a number in a blog post.
Want this set up properly the first time?
Configured properly means a dedicated scoped credential, a secure protocol, a staging branch, and a rollback path for the deploy that goes wrong at 5pm on a Friday. That is standard work on any site we build or maintain. Send us the repository and tell us who your host is. Talk to us on WhatsApp at +254 722 334 188, email hello@wpfoss.ke, or call +254 709 384 200.
Book a Free ConsultationRelated: WordPress Development Kenya ยท Website Maintenance Kenya ยท GitHub Actions docs ยท FTP-Deploy-Action