Drupalwoo ☉

Deploying Drupal With Git For Reliable Team Workflows

Git gives a Drupal team a shared record of changes, but a repository alone does not create a safe deployment process. Drupal combines PHP code, Composer dependencies, configuration, database content, uploaded files, and environment-specific settings. Each part needs a clear place in the workflow.

A workable process lets developers build locally, review changes, test them in a non-production environment, and release the same code to production with minimal manual intervention. The approach should also make it possible to identify who changed a feature, why it changed, and how to reverse the release if a problem appears.

This matters for small Australian agencies as much as for large in-house teams. A Drupal project managed by developers in Sydney and Melbourne may have different working hours, while a client in Perth may expect a release window outside local business hours. Consistent Git conventions reduce the risk created by distance, handovers, and changing project responsibilities.

The most dependable setup treats Git as the source of truth for deployable code and configuration, while treating the production database and public files as managed runtime data. Once those boundaries are documented, Drupal deployments become easier to test, audit, and repeat.

Define What Git Should Track

A Drupal repository should contain the application code required to build the site. This normally includes custom modules, custom themes, configuration files, Composer manifests, patches, deployment scripts, and selected installation files. Contributed modules and themes should generally be declared in composer.json and resolved through composer.lock, rather than edited directly inside the repository.

The web root also needs careful treatment. With a Composer-based Drupal project, the recommended structure commonly places the Drupal web root in a directory such as web/. The repository root holds composer.json, composer.lock, and project tooling, while the web server points to the web directory. This reduces accidental exposure of files such as .env, Git metadata, and development documentation.

Uploaded media should not normally be committed to Git. The sites/default/files directory can become very large and may change outside a code release. Store it on persistent server storage, synchronise it with a managed file service, or copy it through a separate deployment process. A .gitignore file should exclude local files, generated assets, private files, and machine-specific settings.

A practical repository usually excludes the following items:

Use Composer And Lock Files Consistently

Composer is central to modern Drupal development because it records dependencies and their versions in a repeatable way. Developers should change dependencies with Composer commands, commit both composer.json and composer.lock, and allow the deployment system to install the locked versions.

The lock file prevents one developer from testing with a slightly different package version from the one installed on production. In a deployment environment, use composer install rather than composer update. The install command reads the committed lock file and retrieves the exact dependency set. Running composer update during a release can introduce unreviewed changes and make a rollback difficult.

Teams should agree on a supported PHP version, Composer version, and Drupal core release policy. These requirements can be declared in Composer configuration and checked in continuous integration. A project targeting PHP 8.3 locally but PHP 8.1 in production may pass local tests while failing during the actual release.

Security updates deserve a defined process. Drupal security advisories and Composer audit tools can identify vulnerable packages, but automatic updates should still pass the project’s tests. For Australian organisations handling customer data under the Privacy Act, dependency review is part of operational risk management rather than a purely technical exercise.

Separate Configuration From Content

Drupal configuration includes content types, fields, views, image styles, roles, text formats, site settings, and many module options. With the Configuration Management system, this data can be exported to YAML and committed to Git. A typical command is:

drush config:export --destination=../config/sync

The exported files should be reviewed like source code. A configuration change can affect access permissions, search behaviour, forms, or the display of customer-facing content. Descriptive commits make it easier to understand why a view or field was changed and which issue requested it.

Configuration is not identical to content. A new content type belongs in configuration, while an individual article, event, product, or media item belongs in the database. Deploying configuration should not overwrite editorial content. Teams should establish a reliable order for releases: deploy code, import configuration, run database updates, rebuild caches, and then run smoke tests.

Environment-specific values need a separate mechanism. Database credentials, SMTP passwords, private service URLs, and API tokens should be supplied through environment variables, hosting secrets, or protected settings files. A common pattern is to keep a version-controlled settings.php template and load local or production overrides from files outside the repository.

The following configuration rules prevent frequent deployment errors:

Build A Branch And Review Workflow

A team workflow should make the safe path the easiest path. Developers can create short feature branches from the main development branch, implement a focused change, and open a pull request when the work is ready. The pull request should include the Drupal issue reference, configuration notes, test instructions, and any required database update.

Small branches are easier to review than large collections of unrelated changes. A branch that changes a custom module, a Twig template, and several views should explain the relationship between those changes. If configuration is exported, reviewers should inspect the YAML diff rather than accepting it as generated noise.

Protect the production branch with required reviews and automated checks. Direct pushes should be restricted, particularly on projects maintained by external agencies or rotating contractors. Commit messages should describe the change in plain language, while tags can identify releases such as 2025.04.1.

Teams working across Australian time zones can use pull requests as a durable handover record. A developer finishing in Melbourne can leave test evidence and deployment notes for a colleague beginning in Perth or Brisbane, without relying on a message that is buried in chat. Release windows can also be recorded in AEST or AEDT to avoid confusion when daylight saving changes affect Sydney and Melbourne.

A useful pull request check covers:

Automate Testing Before Deployment

Continuous integration should build the application in a clean environment rather than testing the developer’s existing machine. A pipeline can check out the branch, install the locked Composer dependencies, validate PHP syntax, run coding standards, and execute unit or kernel tests. Where practical, functional browser tests should cover login, search, forms, navigation, and important editorial workflows.

Drupal projects benefit from testing configuration imports early. A broken YAML file, missing dependency, or invalid plugin definition can stop a release. Running drush config:import --yes in a disposable environment exposes these errors before production is involved.

A deployment pipeline should also verify that the expected PHP extensions and system tools are available. Image processing, search integration, queues, and PDF generation may depend on services outside Drupal. A pipeline that tests only PHP code may miss a failure caused by a missing binary or an inaccessible endpoint.

Performance checks can be proportionate to the project. A high-traffic government or retail site may need load testing and cache verification, while a small community website may need only a scripted smoke test. For a Sydney-based online retailer, checking checkout and stock-related integrations before a weekend campaign can be more valuable than adding a large suite of low-risk unit tests.

Promote The Same Build Through Environments

The code tested in staging should be the code released to production. Rebuilding dependencies separately in each environment can produce differences, even when the Git commit is identical. A stronger pattern creates a release artifact or container after tests pass, then promotes that artifact through staging and production.

A typical release sequence is:

  1. Fetch the approved release or artifact.
  2. Put the site into an appropriate maintenance or deployment state.
  3. Run database updates with drush updatedb.
  4. Import approved configuration with drush config:import.
  5. Rebuild caches using drush cache:rebuild.
  6. Run smoke tests and inspect logs.
  7. Re-enable normal traffic and monitor the site.

The exact order may vary by hosting platform and project design. Database updates must be compatible with the code being deployed, and configuration imports may depend on new modules or schema changes. Drupal update hooks should be committed with the code that requires them.

Deployments should be designed for brief, predictable interruptions. A managed platform, blue-green setup, or rolling release can reduce downtime for larger sites. A smaller Australian business might instead schedule a release during a quiet period after local support hours, avoiding public holidays and the busy end-of-financial-year period when staff and clients are often unavailable.

Handle Databases, Files, And Secrets Safely

Git cannot roll back a production database in the same way it rolls back code. Before a release involving schema changes or data transformations, create a verified database backup and confirm that restoration is possible. A backup that has never been tested is an assumption, not a recovery plan.

Database updates should be backward-aware where possible. If a deployment removes a field or changes a data structure, the team may need a staged process: add the new structure, migrate data, switch application code, and remove obsolete elements in a later release. This approach gives the team more options when a release must be reversed.

Public and private files require their own backup and retention policies. The public files directory may be reproducible from an asset store, while private files can contain invoices, identity documents, or other sensitive material. Access controls, encryption, and data residency requirements should be reviewed with the organisation’s hosting provider. Australian businesses may prefer a hosting arrangement that keeps operational data in an Australian region, particularly when contractual or privacy obligations apply.

Secrets should be delivered at runtime through the hosting platform or a secrets manager. Rotate credentials when staff leave, vendors change, or a secret is exposed. Logs must be checked as well, because an application can accidentally print connection strings or tokens even when the repository itself is clean.

Plan Rollbacks And Ongoing Operations

A rollback plan should cover more than checking out an earlier Git commit. If the release changed database schema or content, reverting code without addressing those changes can cause further errors. The release record should identify the code version, database updates, configuration changes, backup location, and commands used during deployment.

For low-risk code changes, the team may redeploy the previous artifact. For a database migration, restoration from backup or a forward-fix release may be safer. Drupal update hooks are generally intended to move a site forward, so teams should not assume that running an older code version will reverse them automatically.

After each release, monitor application logs, PHP errors, queue processing, cron execution, cache hit rates, uptime, and key business transactions. A deployment may appear successful while scheduled tasks fail several hours later. Drush status checks and external monitoring can provide early warning before editors or customers report a problem.

Keep a short operational runbook in the repository or team knowledge base. It should explain environment names, deployment permissions, backup procedures, emergency contacts, and release commands. For agencies serving clients in Adelaide, Canberra, or regional New South Wales, clear documentation makes support possible when the original developer is unavailable and ensures that every release follows the same controlled path.