How to Deploy a Website from GitHub to cPanel Using GitHub Actions
Oct 24, 2026
•
By Alex Grant


If you develop websites, you probably know how repetitive manual deployment can become.
You make a change to your website, build the project, open cPanel File Manager, upload the updated files, and check whether everything works.
Repeat this process every time you update the website.
While this workflow works for small projects, it becomes inefficient as your website grows.
A better approach is to automate deployment.
With GitHub Actions, you can configure a workflow that automatically builds your website and uploads the generated files to your cPanel hosting account whenever you push changes to a GitHub repository.
This means you can focus on development while GitHub handles the deployment process.
In this tutorial, we'll create a deployment workflow using GitHub Actions and SSH. The workflow will build a static website and publish its files to your cPanel document root.
By the end, you'll have a simple continuous deployment pipeline for your website.
What we'll build
The deployment process will look like this:
Developer | | Push changes to GitHub v GitHub Repository | | GitHub Actions starts v Build Website | | Generate production files v SSH Deployment | | Upload and extract archive v cPanel Hosting | v Live Website
This approach works well for static websites built using tools such as Vite, Astro, or similar frameworks.
It can also work for simple PHP websites, provided you deploy the correct application files.
Important: This tutorial is for websites that can be built into static files. Applications requiring a persistent Node.js server, server-side rendering, or background processes need a different deployment setup.
Prerequisites
Before starting, make sure you have the following:
GitHub account: Stores your website's source code
GitHub repository: Contains the website project
cPanel hosting account: Hosts the production website
SSH access: Allows automated deployment
SSH key pair: Authenticates GitHub Actions
Node.js and npm: Builds the example website
Domain name: Points visitors to your website
You also need a cPanel hosting account that supports SSH access.
Not all hosting providers enable SSH on every plan. cPanel documents that shell access can be enabled or disabled by the hosting provider. cPanel & WHM Documentation
If SSH access is unavailable, this particular workflow will not work until your hosting provider enables it.
Step 1: Prepare Your Website for Deployment
For this tutorial, we'll assume you have a website built using a Node.js-based frontend framework.
For example, your project might look like this:
my-website/ ├── src/ ├── public/ ├── package.json ├── package-lock.json ├── vite.config.js └── index.html
The project should contain a build command in its package.json.
For a typical Vite project, the script looks like this:
{
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
}
}
The exact configuration may differ depending on your framework.
Build your website locally
Open your terminal in the project directory.
Run:
npm ci
This installs the project's dependencies using the lockfile.
Next, build the website:
npm run build
For a typical Vite project, the generated production files are stored in a directory named:
dist/
The dist directory contains the files that need to be deployed.
For example:
dist/ ├── index.html ├── assets/ │ ├── index-abc123.js │ └── index-def456.css └── favicon.ico
The source code itself does not need to be uploaded to the public website directory.
If you're using another framework, check its build output directory. Astro and other static site generators may have their own output configuration.
Step 2: Enable SSH Access in cPanel
Log in to your cPanel account.
Look for:
Security → SSH Access
Depending on your hosting provider, the interface may be slightly different.
If SSH access is disabled, contact your hosting provider and request access.
You'll need the following information:
- SSH hostname
- SSH port
- cPanel username
- Website document root
For example:
SSH Host: server.example.com SSH Port: 2222 Username: mycpaneluser Document Root: /home/mycpaneluser/public_html
These are example values. Use the actual values provided by your hosting company.
Find your document root
The document root is the directory from which your website's files are served.
For a primary domain, it is commonly:
public_html/
For an addon domain or subdomain, the document root may be different.
You can usually find the correct path in cPanel's Domains interface.
For this tutorial, we'll use:
/home/mycpaneluser/public_html
Replace this with your actual document root.
Step 3: Create an SSH Deployment Key
GitHub Actions needs a secure way to authenticate with your hosting account.
We'll use SSH key authentication instead of storing your cPanel password.
Open a terminal on your local computer.
Run:
ssh-keygen -t ed25519 -C "github-actions-deploy" -f ./hostomy-deploy-key -N ""
This creates two files:
hostomy-deploy-key hostomy-deploy-key.pub
The private key must remain secret.
The public key can be added to your hosting account.
Import the public key into cPanel
- Log in to cPanel.
- Open Security → SSH Access.
- Click Manage SSH Keys.
- Select Import Key.
- Enter a name such as
github-actions-deploy. - Paste the contents of
hostomy-deploy-key.pubinto the public key field. - Import the key.
Now locate the imported key in the Public Keys list.
Click Manage and select Authorize.
This step is essential. cPanel requires newly imported public keys to be authorized before they can authenticate SSH connections. cPanel & WHM Documentation
Test SSH access
Before configuring GitHub Actions, test the connection from your local computer.
Run:
ssh -p 2222 \ -i ./hostomy-deploy-key \ [email protected]
Replace the hostname, port, username, and key path with your actual values.
If the connection succeeds, you should be able to access your hosting account's shell.
If it fails, resolve the SSH issue before continuing.
Step 4: Configure GitHub Repository Secrets
We should never place SSH credentials directly inside a GitHub Actions workflow.
Instead, we'll use GitHub Actions secrets.
GitHub provides repository-level secrets that workflows can access when explicitly configured. GitHub Docs
Open your GitHub repository.
Navigate to:
Settings → Secrets and variables → Actions
Click New repository secret.
Create the following secrets.
DEPLOY_HOST=server.example.com
DEPLOY_PORT=2222D
EPLOY_USER=mycpaneluser
DEPLOY_PATH=/home/mycpaneluser/public_html
DEPLOY_TMP=/home/mycpaneluser/tmp/hostomy-deploy
DEPLOY_SSH_PRIVATE_KEY=Contents of the private SSH key
DEPLOY_KNOWN_HOSTS=Verified SSH host key entry
Add the private key
Open the private key file:
cat hostomy-deploy-key
Copy its entire contents.
Paste them into the GitHub secret:
DEPLOY_SSH_PRIVATE_KEY
Do not commit this file to your repository.
Do not paste it into your website's source code.
Do not print it in GitHub Actions logs.
Configure SSH host verification
SSH host verification helps ensure that GitHub Actions connects to the intended server.
You can obtain the server's SSH host key using:
ssh-keyscan -p 2222 -t ed25519 server.example.com
However, do not blindly trust the output of ssh-keyscan.
Verify the host key fingerprint against information supplied by your hosting provider or another trusted source before using it.
After verification, store the host key entry in:
DEPLOY_KNOWN_HOSTS
For a non-standard SSH port, the known-hosts entry normally includes the hostname and port in bracket notation.
For example:
[server.example.com]:2222 ssh-ed25519 AAAA...
The example key above is illustrative, not a real server key.
Step 5: Create the GitHub Actions Deployment Workflow
Now we'll create the automation that builds and deploys the website.
In your GitHub repository, create the following file:
.github/workflows/deploy.yml
Add this workflow:
name: Deploy Website to cPanel
on:
push:
branches:
- main
permissions:
contents: read
concurrency:
group: production-deployment
cancel-in-progress: false
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v7
- name: Setup Node.js
uses: actions/setup-node@v7
with:
node-version: 22
cache: npm
- name: Install dependencies
run: npm ci
- name: Build website
run: npm run build
- name: Package website
run: tar -czf site.tar.gz -C dist .
- name: Configure SSH
env:
SSH_PRIVATE_KEY: ${{ secrets.DEPLOY_SSH_PRIVATE_KEY }}
SSH_HOST_KEY: ${{ secrets.DEPLOY_KNOWN_HOSTS }}
run: |
mkdir -p ~/.ssh
chmod 700 ~/.ssh
printf '%s\n' "$SSH_PRIVATE_KEY" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
printf '%s\n' "$SSH_HOST_KEY" > ~/.ssh/known_hosts
chmod 600 ~/.ssh/known_hosts
- name: Upload and deploy website
env:
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
DEPLOY_PORT: ${{ secrets.DEPLOY_PORT }}
DEPLOY_USER: ${{ secrets.DEPLOY_USER }}
DEPLOY_PATH: ${{ secrets.DEPLOY_PATH }}
DEPLOY_TMP: ${{ secrets.DEPLOY_TMP }}
run: |
ssh \
-p "$DEPLOY_PORT" \
-i ~/.ssh/id_ed25519 \
-o IdentitiesOnly=yes \
-o StrictHostKeyChecking=yes \
"$DEPLOY_USER@$DEPLOY_HOST" \
"mkdir -p '$DEPLOY_TMP' '$DEPLOY_PATH'"
scp \
-P "$DEPLOY_PORT" \
-i ~/.ssh/id_ed25519 \
-o IdentitiesOnly=yes \
-o StrictHostKeyChecking=yes \
site.tar.gz \
"$DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_TMP/site.tar.gz"
ssh \
-p "$DEPLOY_PORT" \
-i ~/.ssh/id_ed25519 \
-o IdentitiesOnly=yes \
-o StrictHostKeyChecking=yes \
"$DEPLOY_USER@$DEPLOY_HOST" \
"tar -xzf '$DEPLOY_TMP/site.tar.gz' -C '$DEPLOY_PATH' && rm -f '$DEPLOY_TMP/site.tar.gz'"
How this workflow works
Let's break down the important parts.
1. Trigger
on:
push:
branches:
- main
The workflow runs whenever changes are pushed to the main branch.
GitHub Actions supports branch-specific workflow triggers through the push event. GitHub Docs
2. Build
- name: Build website run: npm run build
This generates the production version of the website.
3. Package
- name: Package website run: tar -czf site.tar.gz -C dist .
This creates a compressed archive containing the generated website files.
The archive is easier to transfer than uploading many individual files.
4. SSH configuration
The workflow creates a private key file and configures SSH to verify the server's host key.
5. Upload
The scp command transfers the archive to the cPanel account.
6. Deployment
The final SSH command extracts the archive into the website's document root.
The archive is removed after extraction.
Important security note
The workflow uses version tags for readability.
For production environments, review the source of each GitHub Action and consider pinning actions to full commit SHAs. GitHub recommends SHA pinning as a way to reduce risks associated with mutable action tags. GitHub
Also, verify that your selected Node.js version is compatible with your project's dependencies.
Step 6: Deploy Your First Website
Now that the workflow is configured, let's test it.
First, make sure your repository contains:
.github/
└── workflows/
└── deploy.yml
Your project should also contain:
package.json package-lock.json
And your website should build successfully.
Push the workflow to GitHub
Run:
git add . git commit -m "Add automated cPanel deployment" git push origin main
Open your GitHub repository.
Navigate to:
Actions
You should see a workflow named:
Deploy Website to cPanel
Click the workflow run.
If everything is configured correctly, the workflow will:
- Check out your repository.
- Install dependencies.
- Build the website.
- Package the generated files.
- Connect to your cPanel account.
- Upload the archive.
- Extract the website files.
Once the workflow completes successfully, open your website in a browser.
Your website should now display the latest version.
Test automatic deployment
Make a small change to your website.
For example, update the homepage heading:
<h1>Welcome to My Website</h1>
Change it to:
<h1>Welcome to My Updated Website</h1>
Commit and push the change:
git add . git commit -m "Update homepage heading" git push origin main
GitHub Actions should automatically deploy the updated website.
You no longer need to manually upload the generated files through cPanel File Manager.
Step 7: Troubleshoot Common Deployment Problems
Automated deployments can fail for several reasons.
Here are some common issues and how to resolve them.
Error: Permission denied (publickey)
This usually means SSH authentication failed.
Possible causes:
- The public key was not imported into cPanel.
- The key was imported but not authorized.
- The wrong private key is stored in GitHub.
- The username is incorrect.
Solution:
Verify that the public key is authorized in cPanel.
Check that DEPLOY_SSH_PRIVATE_KEY contains the correct private key.
Also confirm that the SSH username matches your cPanel account.
Error: Host key verification failed
This means SSH could not verify the server's identity.
Possible causes:
- The known-hosts entry is incorrect.
- The SSH port is wrong.
- The server's host key has changed.
- The host key was obtained from an untrusted source.
Solution:
Verify the server's actual host key fingerprint with your hosting provider.
Update DEPLOY_KNOWN_HOSTS only after verifying the new key.
Do not disable host key verification to make the deployment work.
Error: Connection timed out
This usually indicates a network or SSH configuration issue.
Check:
- Is SSH enabled on your hosting account?
- Are you using the correct SSH port?
- Is your hosting provider blocking external SSH connections?
- Does the server allow connections from GitHub-hosted runners?
If your hosting provider restricts SSH connections by IP address, you may need to configure an appropriate access policy.
Error: No such file or directory: dist
The workflow expects the website build output to exist in:
dist/
However, some frameworks use different output directories.
For example, a framework might generate:
build/
or:
out/
Solution:
Check your framework's build configuration.
If the output directory is different, update the packaging command accordingly.
For example:
tar -czf site.tar.gz -C build .
Error: Website displays an old version
Possible causes include:
- Browser caching.
- CDN caching.
- Deployment to the wrong document root.
- The build output was not updated.
Check the deployment logs and confirm the correct directory is being updated.
If you're using a CDN, purge its cache after deployment when necessary.
Error: Old files remain after deployment
The workflow extracts the archive over the existing website directory.
This overwrites matching files, but it does not automatically delete files that are no longer present in the build output.
For example, if your old build contains:
assets/old-script.js
and the new build no longer contains that file, the old file may remain on the server.
This behavior is intentional in the example workflow because automatically deleting production files can be dangerous.
If you need a deployment process that removes obsolete files, consider a carefully tested release-based deployment or a controlled synchronization strategy.
Do not run an unrestricted recursive deletion command against your document root without a verified backup.
Step 8: Secure Your Automated Deployment
Automated deployment introduces a new way to access your hosting account.
It should be configured carefully.
Use SSH keys instead of passwords
SSH key authentication avoids storing your cPanel password in the workflow.
Use a dedicated deployment key and revoke it when it is no longer needed.
Remember that an SSH key authorized for a cPanel account may have access to more than just the website's document root. Review the privileges associated with the account.
Protect your GitHub secrets
Never commit private SSH keys, passwords, or API credentials to your repository.
Use GitHub Actions secrets.
Also avoid exposing deployment secrets in logs.
Restrict deployment to trusted branches
The example workflow deploys only when changes are pushed to main.
This helps prevent accidental deployment from development branches.
Do not expose production secrets to untrusted pull request workflows.
Verify the SSH host key
Always verify the server's identity before allowing GitHub Actions to connect.
Avoid disabling:
StrictHostKeyChecking
A secure deployment should verify both the client's credentials and the server's identity.
Keep backups
Before introducing automated deployment, create a backup of the existing website.
For WordPress or other dynamic applications, remember that a website backup may need both files and a database backup.
A deployment workflow that only copies static files does not provide a complete recovery strategy for a dynamic website.
Frequently Asked Questions
Can I deploy a WordPress website from GitHub to cPanel?
Yes, but the workflow depends on what you are deploying.
If you're deploying a custom WordPress theme, plugin, or static frontend, GitHub Actions can automate the process.
However, a complete WordPress website also requires its database and configuration. Simply uploading a static build will not deploy a complete WordPress installation.
Can I use GitHub Actions with shared hosting?
Yes, provided your hosting account supports a suitable deployment method.
SSH is one option.
Other hosting accounts may support FTP or SFTP deployment, although SSH-based workflows offer additional flexibility.
Does this method require a VPS?
No.
This tutorial uses cPanel shared hosting.
A VPS is not required for a static website that can be built into HTML, CSS, and JavaScript files.
However, applications requiring a persistent server-side runtime may need VPS or application hosting.
Can I deploy automatically whenever I push to GitHub?
Yes.
The workflow is configured to run whenever changes are pushed to the main branch.
You can change the branch name or add additional deployment conditions in the workflow configuration.
Is GitHub Actions free?
GitHub Actions has usage limits and billing rules that depend on your GitHub plan, repository type, and runner configuration.
Check GitHub's current documentation for the applicable limits.
Conclusion
Manually uploading website files to cPanel can become repetitive as your project grows.
By combining GitHub Actions with SSH, you can automate the build and deployment process.
The workflow in this tutorial provides a simple way to:
- Build your website automatically.
- Store deployment credentials securely.
- Upload production files to cPanel.
- Deploy updates whenever you push changes to GitHub.
This is particularly useful for developers working with static websites, frontend frameworks, and projects hosted on traditional shared hosting.
Once the deployment process is working, you can extend it with testing, staging environments, release management, and more advanced deployment strategies.
Deploy your website with Hostomy
If you're building a website and want a hosting environment that fits your development workflow, explore Hostomy's hosting options.
For a workflow like this, check that your chosen hosting plan supports SSH access and allows the commands required for deployment.
If your application requires a persistent Node.js server, background workers, or other server-side processes, consider a hosting environment designed for those workloads.
Choose the hosting setup that fits your application rather than adding unnecessary infrastructure.