Git deployments
Put a site live from a Git branch instead of uploading files. Each deploy fetches the branch from GitHub, GitLab, Gitea, Bitbucket or any Git server, runs your build commands (Composer, npm, Artisan...) as the hosting account in a new release, and switches the site to it in one step. A push can deploy automatically, and going back to an earlier release takes a click.
reqad deploy on the command line, and the REST API. A deployment is for one site: the main domain of an account or one of its addon domains. Each site on the page shows its code (Git, or the files uploaded to it) and its runtime (PHP, or a Node.js / Python app), and one Settings form changes both.deployments=1 in /usr/local/reqad/etc/server-software.ini; the Deployments page then appears under Accounts & Domains. While it is off, apps that already run keep running, but the page, reqad deploy, reqad app, their API endpoints and push webhooks are turned off.How a deploy works
- Fetch. The server keeps a copy of the repository in
~/apps/<domain>/repo.gitand fetches the new commits. - New release. The branch is unpacked into
~/apps/<domain>/releases/<time>/, without the.gitfolder. - Shared files. The paths you list as shared, such as
.envorstorage/, are linked into the release from~/apps/<domain>/shared/, so they survive every deploy. - Build. Your build commands run in the release as the account, with the account's PHP version. The first command that fails stops the deploy, and the site stays as it was.
- Switch.
~/apps/<domain>/currentis pointed at the new release in one step, so visitors never see a half-copied site. The site's document root (public_html, or the addon domain's folder) points atcurrent, or at its public folder. - Health check (optional). A page you choose, like
/, must answer. If it does not, the site goes back to the previous release by itself. - Clean up. Old releases beyond the number you keep (5 by default) are removed.
On the first deploy, the files the site had are moved aside to <document root>.before-deploy-<time>. Nothing is deleted. The document root keeps its path, so backups, the WordPress Toolkit and the file manager keep working as before.
A deploy runs in the background with limits on memory (2 GB), processes and time (30 minutes). Only one deploy of a site runs at a time. Pushes that arrive meanwhile are merged into one next deploy.
The Deployments page
Each site on the page is a card with two halves:
| Part | What it shows | Buttons |
|---|---|---|
| Code | The repository and branch, the commit that is live and when it went live, and how the last deploy ended. Without Git: Files uploaded to the site. | Deploy, Deploy log, Roll back, Connect (the deploy key and webhook to paste into your Git host) |
| Runtime | The PHP version, or the Node.js / Python app with its state (running, not listening, crashing, stopped), start command, port and memory. | For an app: Start / Stop / Restart, App log, Environment |
Settings on a card opens the same form as New deployment. Below the sites, Runtimes installs and updates the Node.js and Python versions apps use.
Setting one up
- New deployment: choose the site.
- Optionally pick a Start from preset. It fills in the settings below, and you can change anything after.
- Code from Git: give the repository address and branch, and the build settings. Switch it off to keep the files uploaded to the site, for example for a Node.js app you upload over SFTP.
- Runtime: PHP serves the site with its PHP-FPM pool, as before. Choose a Node.js or Python version to run the site as an app, and give its start command.
- Create, then Connect shows what to paste into your Git host:
- the site's deploy key, which gives read access to a private repository (GitHub: Settings > Deploy keys; GitLab: Settings > Repository > Deploy keys; Gitea: Settings > Deploy Keys);
- the webhook address and secret, for deploys on push. Use content type
application/jsonand push events. On GitLab the secret goes in Secret token.
- Deploy puts the branch live. The log follows the deploy as it runs. An app on the site starts with its first deploy.
Use the SSH address of a private repository (git@github.com:you/site.git). An HTTPS address needs no key, so it suits public repositories.
In Settings, switching Code from Git off stops deploying (the site keeps its current release). Setting the runtime back to PHP removes the app, and the domain is served by PHP again. Remove does both. The files always stay.
Start from
A preset fills in common settings for a kind of site. Nothing is saved until you press Create or Save.
| Preset | Build commands | Shared | Public folder | Runtime |
|---|---|---|---|---|
| PHP | composer install --no-dev --optimize-autoloader | .env | the repository's root | PHP |
| Laravel | composer install --no-dev --optimize-autoloader, php artisan migrate --force, config:cache, route:cache, view:cache | .env, storage/ | public | PHP |
| Static site | npm ci, npm run build | - | dist | PHP (nginx serves the built files) |
| Node.js app | npm ci, npm run build --if-present | .env | - | the newest installed Node.js, start command npm start |
| Python app | python -m venv .venv, .venv/bin/pip install -r requirements.txt | .env | - | the newest installed Python, start command .venv/bin/gunicorn -w 2 -b 127.0.0.1:$PORT app:app |
The Node.js and Python presets need that runtime installed first (Runtimes at the bottom of the page). They are offered only with nginx in front. Change the start command to match your app: for example node server.js, or .venv/bin/uvicorn main:app --host 127.0.0.1 --port $PORT for FastAPI. For Django, use gunicorn with your project's wsgi module.
Git settings
| Setting | What it does |
|---|---|
| Branch | The branch that is deployed. Webhook pushes to other branches are ignored. |
| Build commands | Shell commands, one per line, run in the new release as the account. Examples: composer install --no-dev --optimize-autoloader, npm ci + npm run build, php artisan migrate --force. $REQAD_SHARED is the shared folder and $REQAD_RELEASE the new release. |
| Shared between releases | One path per line; end folders with /. A shared path is taken from the first release that has it. A path missing from the release starts empty. Typical: .env, storage/, wp-content/uploads/. |
| Public folder | The folder of the repository the web serves, e.g. public (Laravel, Symfony) or dist (built front-ends). Empty: the repository's root. |
| Health check | A path that must answer 2xx or 3xx after the switch. If it does not, the site goes back to the previous release. |
| Releases kept | 2 to 20. One is live; the others are there to roll back to. |
| Deploy on push | Off: the webhook is accepted, but only Deploy, the command line or the API start a deploy. |
Rolling back
Roll back lists the releases on disk with their commits. Choose one and the site switches to it at once. Nothing is rebuilt, and the shared files stay as they are. The next deploy puts the branch live again. A database migration is not undone by a rollback.
Webhooks
Git hosts call https://<server hostname>/.reqad-deploy/<id> on port 443. A webhook starts a deploy only when it is signed with the deployment's secret (GitHub, Gitea/Forgejo and Bitbucket sign it; GitLab sends the secret as its token). The panel port 2087 can stay closed to the internet (Settings > Security > IP restriction). Webhooks need nginx in front.
From a CI pipeline, call the API instead. Use an API token with the write permission; a token for the site's account is enough:
curl -X POST -H "Authorization: Bearer $REQAD_TOKEN" https://server.example.com:2087/api/v1/deployments/shop.example.com/deploy
Good to know
- Build commands run as the hosting account, never as root, with the same access as the account's own PHP.
composer,npmandnodeare the server's. - A Node.js or Python app on the domain restarts after each deploy. If it does not start, the deploy goes back to the previous release.
- Git submodules are not fetched.
- No other site of the account may have its document root inside a deployed one, because each deploy replaces that folder. Creating such an addon domain is refused.
- Releases count toward the account's disk usage and its backups. Lower Releases kept for big builds (e.g.
node_modules). - The last deploy failing shows on the dashboard's Needs attention card.
- Delete stops deploying. The site keeps running from its last release, and its files stay in
~/apps/<domain>.
On the command line
reqad deploy create shop.example.com --repo=git@github.com:you/shop.git \
--dir=public --shared=".env,storage/" --check=/ \
--build="composer install --no-dev --optimize-autoloader
php artisan migrate --force"
reqad deploy run shop.example.com --wait # follow the output
reqad deploy rollback shop.example.com # to the release before
reqad deploy list
reqad deploy log shop.example.comSee Command line (CLI) for every option.
Need a hand? Create a free account or contact us.