Creating a service in .services.ymlfor Drupal 11 , 10 , 9 , and 8

Last updated :  

Think of Drupal's service container as a hotel concierge desk. You don't personally know how to book a taxi, reserve a restaurant table, or arrange a wake-up call — you just ask the concierge, and they hand you exactly what you need, already configured and ready to use. A service is exactly that: a reusable object registered once, in one place, that any other part of your code can request by name instead of constructing by hand.

What you'll learn in this lesson

  • What a service actually is, and why Drupal core is built almost entirely out of them
  • How to register a brand-new service in your module's .services.yml file
  • What the class and arguments keys really do
  • Where to find a real, working example in Drupal's own Examples project

Why services exist

Without services, every class that needed to run a database query would have to know how to open its own database connection; every class that needed to send an email would duplicate mail-sending logic. Drupal instead builds one shared object for each responsibility — database, current_user, entity_type.manager, and hundreds more — and hands it out to whoever asks. Your own custom module can add its own services the exact same way core does.

Registering a service: the real pattern

Here's a real service definition from Drupal's own Examples module, in dbtng_example.services.yml:

services:
  dbtng_example.repository:
    class: Drupal\dbtng_example\DbtngExampleRepository
    arguments: ['@database', '@string_translation', '@messenger']

The service ID

dbtng_example.repository is the service's unique name — by convention, {module_name}.{descriptive_name}. This is the string you'll use everywhere else to ask the container for this exact object.

class

The fully-qualified PHP class that gets instantiated when something requests this service. Drupal never calls new on this class directly in your code — the container does it once, then hands the same instance to everyone who asks (services are singletons by default).

arguments

A list of other services (each prefixed with @) to pass into this service's constructor, in order. Here, @database, @string_translation, and @messenger are three of Drupal core's own built-in services — the container resolves each one and passes it straight into DbtngExampleRepository's constructor. You never write the wiring code yourself; you just declare what you need.

Don't forget to clear the cache. Drupal scans and caches the full list of registered services at build time. Add a new service, and it simply won't exist as far as the container is concerned until you run drush cache:rebuild (or clear caches from the admin UI).

Quick check: if two different classes both need to send a custom-formatted notification email, and you write that logic once as a service, how many times does the container actually instantiate that service class across a single page request? (Answer: once — services are singletons per request. Both classes are handed the same shared instance, not separate copies.)

Key takeaways

  • A service is a reusable PHP object registered once in {module_name}.services.yml and shared by everything that requests it.
  • The service ID convention is {module_name}.{descriptive_name} — always namespaced to your module to avoid collisions.
  • arguments lists other services (prefixed @) that the container injects into the constructor automatically — you never wire this by hand.
  • Services are singletons: the same instance is reused everywhere it's requested within a request, not recreated each time.
  • Registering a new service always requires a cache rebuild before Drupal will recognize it.

Coming up next

Declaring a service's dependencies in YAML is only half the story — next we'll look at exactly how those @service.name arguments get matched up with your PHP class's constructor parameters, and what happens when a dependency you ask for doesn't exist.