Skip to main content

Mastering custom drush commands

‍The power of CLI in Drupal development

Imagine you’re managing a news website with thousands of articles. One morning, your editor asks, “How many ‘breaking news’ articles do we have?”
Without a custom Drush command, you’d log into the admin UI, filter the content list, and count manually tedious process.
But with a custom command, you simply type:

…and get an answer in seconds!

This is the magic of Drush: transforming time-consuming tasks into instant insights. In this blog, we’ll walk through creating and refining custom Drush commands, showing how they can become indispensable tools in your Drupal workflow.

Why custom drush commands matter

Drush isn’t just a shortcut’s a command-line interface that lets you interact with Drupal at its core. Custom commands extend this power, letting you automate workflows, query data, and manage your site without touching a browser.

Real-world impact

Here’s how Drush can change your workflow:

Building blocks of a custom drush command

Let’s start with a concrete example: a command that counts nodes. This will illustrate the key components and decisions involved in building custom commands.

Project structure‍

This structure follows Drupal’s conventions, ensuring your command is discoverable and maintainable.

Module information file

This metadata tells Drupal what your module does and which versions it supports.

Services configuration‍

Here, you’re defining a service that Drush can recognise. The @entity_type.manager argument gives your command access to Drupal’s entity system.

Learn more about services in Drupal

The command implementation

Let’s look at the code for a custom node-count command, with full validation and helpful output.

How it works

This command uses Drupal’s entity query system to count nodes, varying the result based on the --type option. The entity_type.manager service provides access to this system, making the command reusable and testable.

Official Drush Command Authoring Docs

Integrating into existing modules

You don’t always need a new module. Adding commands to existing ones keeps your codebase clean.

Steps to integrate

This approach avoids bloating your project with unnecessary modules while keeping code organised.

Drupal 10 and 11: modernising your command

As Drupal evolves, so should your commands. Here’s how you can update the same command for Drupal 10/11 using modern PHP features.

Key improvements

These changes reflect Drupal’s shift toward stricter type safety and modern PHP practices.

Real-world example: content audit command

Scenario

A content agency migrates 50,000 articles from a legacy CMS. They need to verify data integrity:

Solution: a custom drush command‍

Why it works

This command leverages Drupal’s entity system to audit taxonomy terms, ensuring the migration team gets immediate feedback on taxonomy field integrity.

Best practices for custom commands

1. Use dependency injection

Injecting services like entity_type.manager makes your command testable and flexible.
Learn more about Dependency Injection in Drupal

2. Provide clear descriptions

Use the @usage annotation to document how users should run the command.

3. Handle edge cases

What happens if a node type doesn’t exist? Add validation:

This ensures your command fails gracefully instead of returning cryptic errors.

Common pitfalls and how to avoid them

Pitfall 1: Missing service tags

Forgetting to add drush.command in services.yml means Drush won’t recognise your command.

Pitfall 2: Overlooking access checks

Using accessCheck(true) ensures the command respects user permissions. Skipping it might expose sensitive data.

Pitfall 3: Hardcoding dependencies

Never hardcode services like \Drupal::entityTypeManager(). Always inject them for better testability.

Troubleshooting: When things go wrong

Case study: missing command after enabling the module

Symptoms: drush list doesn’t show your command.

Diagnosis:

Case study: slow query on large sites

Symptoms: The command takes 10+ seconds to count nodes.

Fix: Add caching:

This adds caching to prevent redundant queries during frequent audits.

Expanding the possibilities: beyond node counting

Custom commands aren’t limited to counting nodes. Let’s explore advanced use cases:

Use case 1: bulk entity updates

A real estate platform uses a command to update property listings when market data changes:

Use case 2: health checks

A healthcare site runs nightly health checks to ensure critical content is up-to-date:

Best practices in action

1. Meaningful aliases

Instead of custom:node-count, use nc for brevity.

2. Usage examples

Always include @usage annotations. For instance:

3. Help text

Describe what the command does in plain language:

Conclusion

Custom Drush commands, when thoughtfully built, do more than automate tasks—they shape a cleaner, faster, and more predictable development workflow. 

By applying best practices like dependency injection, validation, and clear documentation, developers create commands that are easy to maintain and trusted across teams. 

Over time, these tools become integral to managing complexity, improving deployment confidence, and making Drupal development feel less like firefighting and more like engineering.

Ready to go further? Explore the official Drush command authoring docs and Drupal’s services and dependency injection guide to keep levelling up your CLI skills!

Happy Drushing!

We'd love to talk about your business objectives

Written by