cPanel can host Git repositories and deploy from them, which turns "upload the changed files and hope" into a repeatable process with a history you can roll back to. For anyone who edits a site more than occasionally, it is the largest workflow improvement available on shared hosting.
Two things it does
Hosting a repository on your account, which you push to from your own machine.
Deploying: when you push, cPanel can copy the files into your web root automatically.
The second is the point. Without it you have version control and still upload by hand.
Creating one
Under Files, open Git Version Control and create a repository.
Two modes. Clone pulls from an existing remote repository, useful if your code already lives on a hosting service. Create makes an empty repository here that you push to.
Set the repository path outside public_html. The repository contains your version history, and anything inside public_html is served to the internet. A .git directory in a public directory can expose your entire source history including anything ever committed to it.
That is a real and common exposure, and it is entirely avoided by putting the repository above the web root and deploying into it.
Setting up deployment
Add a .cpanel.yml file to the repository root. It tells cPanel what to copy where:
--- deployment: tasks: - export DEPLOYPATH=/home/username/public_html/ - /bin/cp -R * $DEPLOYPATH
Replace username with your account name. The file must be at the repository root, must be named exactly that, and YAML is whitespace-sensitive. A tab instead of spaces makes deployment fail with a message about the file being invalid.
Commit it, push, and use the Manage screen to run a deployment.
Do not deploy everything
The example above copies the whole repository, including the readme, the configuration templates and anything else that belongs in version control but not on a public web server.
Copy specific directories instead, or add a .gitignore and be deliberate about what the deployment task moves. Deploying your entire repository to the web root publishes files you did not intend to publish.
Never commit credentials
Database passwords, API keys and configuration files with secrets do not belong in a repository, and removing them later does not remove them, because the history keeps them.
Keep configuration outside the repository and reference it, or use a template committed without values. This matters more here than on a private repository, because a .git directory in a public path exposes the whole history at once.
What it does not solve
The database. Deployment copies files. Content, settings and user data live in the database and are untouched, which is usually what you want, deploying a development database over a live one would destroy real content.
So a WordPress deployment moves themes and plugins, not posts.
Migrations. If a change needs a schema update, that is a separate step. Deployment will not run it.
Rollback of a broken deployment. You can deploy an earlier commit, which fixes the files. It does not undo anything the broken version did to the database.
A working arrangement
- Repository above
public_html, deploying into it. .cpanel.ymlcopying only what belongs on the server.- Configuration and secrets outside the repository.
- Test on a staging subdomain before deploying to the live site.
- A backup before any deployment that touches something structural.
The staging step is worth the setup. Creating subdomains explains it, and password-protecting the staging copy keeps it out of search results.
When it fails
Deployment does nothing. The .cpanel.yml is missing, misnamed, or not at the repository root.
Invalid YAML. Tabs instead of spaces, or inconsistent indentation.
Permission denied. The deployment path is wrong, or points outside your account.
Files deployed but the site is unchanged. Caching. Clear it before assuming the deployment failed.
Keep the repository out of the served directory
A repository inside the web root exposes its entire history, including anything ever committed and later removed.
curl -sI https://example.com/.git/config | head -1 curl -s https://example.com/.git/HEAD 2>/dev/null | head -1 ls -d ~/public_html/.git 2>/dev/null
A response other than an error on those requests means the whole history is downloadable by anybody, and automated scanners request that path routinely.
The correct arrangement keeps the repository elsewhere and deploys files into the served directory. Where that is not possible, the directory has to be blocked explicitly, which is a rule that can be lost during a migration.
Confirm the deployment actually ran
A push that succeeds and a site that updated are two different events.
git -C ~/repo rev-parse --short HEAD ls -la --time-style=+%F' '%H:%M ~/public_html/index.php cat ~/repo/.cpanel.yml 2>/dev/null
Compare the current commit against the modification time of a deployed file. A file older than the last commit means the deployment step did not run or failed silently.
The deployment definition is worth reading rather than assuming. A file with a syntax error is skipped, and the push reports success either way.
Decide what happens to files that are not in the repository
Deployment copies what is tracked and does nothing about what is already there.
Uploads, cached files and anything the application writes must not be in the repository, and they must not be removed by the deployment either. Getting that wrong produces either a repository full of user content or a deployment that deletes it.
cat ~/repo/.gitignore 2>/dev/null du -sh ~/public_html/uploads 2>/dev/null
List those directories explicitly as excluded, and confirm after the first deployment that they are still present. That check takes seconds and prevents the version of this that loses customer data.
Confirm the branch being deployed is the one you think
A repository has several branches and the deployment uses one, which is easy to lose track of after a period of working locally. Read which branch the deployment is configured against and which one you last pushed to. Work pushed to a branch nothing deploys from produces the specific confusion where the push succeeded, the repository is correct and the site has not changed. Reading the deployed commit against the branch head answers it in one comparison rather than by inspecting files one at a time.