Push-to-Deploy: Git Workflows for Small Teams
Deploying an application should not require manually uploading files every time a developer makes a change. For small teams, a Git-based push-to-deploy workflow can turn deployment into a repeatable process triggered by a simple command.
One of the simplest approaches uses a bare Git repository on the server and a post-receive hook that runs when code is pushed. The hook can check out the selected branch, run deployment steps and activate the new release.
This approach requires no dedicated CI vendor or container registry, making it a practical starting point for teams hosting applications on a VPS.
However, a Git hook is not a complete deployment platform. Testing, dependency installation, secret management, error handling and rollback procedures still need to be designed properly.
1. How a Git Push-to-Deploy Workflow Works
A basic deployment workflow follows this sequence:
A developer updates the application code locally.
The changes are committed to Git.
The developer pushes the selected branch to the server.
Git runs the server-side deployment hook.
The deployment process prepares a new release and runs the required checks.
If the deployment succeeds, the application switches to the new release.
If something goes wrong, the current release remains active or the team restores a known-good release.
The main benefit is consistency. Developers follow the same deployment process instead of relying on manual file uploads and undocumented server changes.
2. Set Up a Bare Git Repository on Your VPS
A bare repository stores Git history without a checked-out working directory. It is useful as a receiving endpoint for pushes from developer machines.
For example, an administrator might create a dedicated deployment user and repository under a directory such as:
/home/deploy/
└── app.git/The repository can then receive code from an authorized development machine.
On the development computer, the server repository can be added as a Git remote:
git remote add production deploy@your-server:/home/deploy/app.gitReplace the username, server address and repository path with your actual configuration.
You can then push the intended branch:
git push production mainThis example assumes that the server account has the necessary SSH access and that the bare repository exists. Configure repository permissions and SSH authentication carefully, and avoid granting the deployment user unnecessary administrative privileges.
3. Automate Deployment with a post-receive Hook
Git supports server-side hooks that run in response to repository events. A post-receive hook can initiate the deployment process after a push has been received.
A typical deployment script performs these tasks:
Identifies the branch being deployed.
Creates a new release directory.
Checks out the intended commit.
Installs production dependencies.
Builds assets where necessary.
Runs appropriate tests and validation.
Activates the release only if the required steps succeed.
Records the deployment result for troubleshooting.
The hook should call a dedicated deployment script rather than becoming a large collection of hard-to-maintain shell commands.
Important: A post-receive hook runs after Git has accepted the pushed references. If the deployment fails, Git does not automatically undo the push. Your deployment script must detect errors and leave the currently active release untouched.
For a practical starting point, see SteboHost's Git deployment setup guide.
4. Use Release Directories for Safer Deployments
One of the most useful improvements is to deploy each version into its own directory rather than overwriting the currently running application.
A release structure might look like this:
/var/www/myapp/
├── releases/
│ ├── 20261013-1400/
│ ├── 20261013-1530/
│ └── 20261013-1615/
├── shared/
│ └── .env
└── current -> releases/20261013-1615/In this example:
releases/contains individual application versions.shared/holds configuration or persistent files that must survive deployments.currentpoints to the release serving the application.
The web server or application service must be configured to use the current path.
When a new release is ready, the deployment script updates the symlink to point to the new directory. If activation fails, the script can retain the previous target.
This approach makes deployments easier to reason about because the old release remains available until it is safe to remove.
Why atomic activation matters
Updating the current symlink using an appropriate atomic replacement technique can reduce the chance that requests encounter a partially updated application.
The deployment script should prepare the new release completely before activation. It should also verify that the new target is valid and that the application can start successfully.
Keep a limited number of previous releases for rollback, and establish a cleanup policy so old files do not fill the VPS disk.
5. Follow Branch Rules That Keep Production Stable
Automation is only as safe as the code being deployed.
For a small team, a straightforward branch policy is often enough:
main: The branch intended for production deployment.Feature branches: Used for individual changes before review and merging.
staging: Optional, for changes that need testing in a separate environment before production.
Keep main deployable. Review changes before merging, run the checks appropriate to the application and deploy only the intended branch.
For a solo developer, even a short review of the diff before merging can catch mistakes such as accidental configuration changes, debugging code or committed secrets.
If staging is required, configure a separate deployment target. Avoid allowing a staging push to overwrite production files or use production credentials unnecessarily.
6. Add Guardrails to Prevent Failed Deployments
A deployment that partially succeeds can be more disruptive than one that fails immediately. Add checks before activating the new release.
Validate before switching
Run syntax checks, tests and build commands before changing the current symlink. If any required command fails, stop the deployment and preserve the existing active release.
Keep secrets out of Git
Production environment variables and credentials should live outside the repository. Store them in an appropriately protected server-side configuration file or secrets-management system.
For a release-directory setup, the deployment process can link the shared configuration into each release without committing secrets to source control.
Record each release
Record the Git commit, deployment time and result. Git tags can help identify known releases, while deployment logs make it easier to investigate failures.
For example, tagging a release might look like this:
git tag -a v1.2.0 -m "Release 1.2.0"
git push origin v1.2.0Use your team's preferred release naming convention, and ensure the relevant tags are available to the people responsible for deployment.
Protect the deployment process
Use SSH key authentication, restrict who can push to the production repository and give the deployment account only the permissions it needs. Review deployment logs and ensure that error messages do not expose secrets.
7. Plan for Rollbacks Before You Need One
A rollback should be a deliberate, tested procedure rather than an improvised recovery during an incident.
With release directories, restoring an earlier application version can involve repointing current to a known-good release. However, this is safe only if that release remains compatible with the current environment and data.
Pay particular attention to:
Database migrations that remove or change columns.
Background jobs running code from a different release.
Uploaded files and other persistent data.
Cache formats and application configuration.
External services that may have changed during deployment.
Keep database changes backward-compatible where practical, and back up important data before risky migrations.
Remember: Reverting the application code does not automatically reverse database changes or restore deleted files.
8. When Should You Move from Git Hooks to CI/CD?
A direct Git hook is a useful starting point, but there comes a point when a team needs more comprehensive automation.
Consider adding a CI/CD service when you need:
Automated tests before production deployment.
Multiple servers or environments.
Preview deployments for pull requests.
Approval gates for production releases.
Centralized deployment logs and notifications.
More advanced build, security scanning or artifact-management workflows.
In a more advanced pipeline, a push triggers automated checks, and a successful pipeline invokes the same tested deployment script.
The underlying principles remain the same: use version-controlled code, prepare releases consistently, keep secrets protected and maintain a dependable rollback procedure.
Git Push-to-Deploy Checklist
Before relying on automated deployment for a production application, verify the following:
The server repository accepts pushes only from authorized users.
Production deployments use the intended branch.
Deployment runs as a dedicated, appropriately restricted user.
Each deployment creates a separate release directory.
Dependencies and assets are prepared before activation.
Failed checks leave the active release untouched.
Environment files and credentials are kept out of Git.
Logs record the commit and deployment result.
Previous releases are retained for rollback.
Database migrations and persistent data have a recovery plan.
The rollback procedure has been tested.
Old releases are cleaned up without deleting the active release or required shared files.
Frequently Asked Questions
What is a Git post-receive hook?
It is a server-side Git hook that runs after a push updates repository references. It can trigger deployment scripts, notifications or other post-push tasks. The hook itself does not provide transactional deployment or automatic rollback; those behaviours must be implemented in the deployment process.
Can I deploy a website directly from Git to a VPS?
Yes. A VPS can receive Git pushes through SSH and use a hook or deployment script to prepare and activate the application. You need to configure permissions, runtime dependencies, the web server and any required services.
Is Git push-to-deploy suitable for Laravel?
Yes, provided the deployment process accounts for Laravel's dependencies, environment configuration, cache handling, database migrations, storage links, queues and application-specific requirements. See the Laravel deployment checklist for a VPS.
Do I need Docker for Git-based deployment?
No. Git-based deployment can work with a conventional application installation or a containerized environment. Docker may improve environment consistency, but it is not a requirement for automating deployments with Git.
Is a Git hook the same as a CI/CD pipeline?
No. A Git hook is a mechanism for triggering a script when a Git event occurs. A CI/CD pipeline typically coordinates additional steps such as automated tests, builds, security checks, approvals and deployment across environments. A hook can still be useful as part of a broader pipeline.
Start Simple and Automate Carefully
A Git-based push-to-deploy workflow can eliminate repetitive manual uploads and make deployments more predictable without requiring a large infrastructure stack.
Start with a dedicated repository, a controlled deployment script, separate release directories and a tested rollback procedure. Add automated testing and more advanced CI/CD tooling when your team's requirements justify the extra complexity.
Explore SteboHost developer hosting and the Git deployment setup guide to plan a workflow suited to your applications and team.
Written by Denis Makokha
SteboHost engineering and operations team. We write practical guides on hosting, domains, email and security for African businesses — based on what we see in the field, not marketing copy.
Ready to get started?
Browse our hosting plans or contact our team for help choosing the right one.