This is the first article in a series showcasing the most important new features introduced by Symfony 8.1, which will be released at the end of May 2026.


Symfony applications are typically built around handling HTTP requests. They do so using the HttpKernel component, which provides a structured process for converting a Request into a Response.

However, not all applications serve HTTP. Console commands, message consumers, and background workers do not need the HTTP layer, but still rely on other parts of Symfony such as the dependency injection container.

Until now, those applications still had to pull in the HttpKernel component, even though they never used it. In Symfony 8.1, after considering this problem for a long time, we're finally making it possible to build HTTP-less Symfony applications.

Building HTTP-less Applications

Nicolas Grekas
Contributed by Nicolas Grekas in #63710

Symfony 8.1 extracts the kernel and bundle infrastructure into the DependencyInjection component, where it belongs, making HTTP-less Symfony applications a reality.

The new Symfony\Component\DependencyInjection\Kernel namespace provides everything needed to build a kernel that boots the container, loads bundles, and reads configuration without any HTTP-related code. To create a headless kernel, extend AbstractKernel and use KernelTrait:

1
2
3
4
5
6
7
8
9
10
// src/Kernel.php
namespace App;

use Symfony\Component\DependencyInjection\Kernel\AbstractKernel;
use Symfony\Component\DependencyInjection\Kernel\KernelTrait;

class Kernel extends AbstractKernel
{
    use KernelTrait;
}

KernelTrait provides the same container lifecycle as MicroKernelTrait (building, compiling, and caching the service container) but without any HTTP logic. It follows the same conventions: config/bundles.php for bundle registration, config/packages/ for configuration, and config/services.yaml (or .php) for service definitions.

Decoupling the Kernel Interface from HTTP

Until Symfony 8.1, HttpKernel\KernelInterface extended HttpKernelInterface. Any service that needed to inspect bundles, the environment, or the cache directory had to type-hint a kernel that also pulled in the HttpFoundation component. Symfony 8.1 introduces a new KernelInterface in the DependencyInjection component that exposes only the container-related API:

1
2
3
4
5
6
7
8
use Symfony\Component\DependencyInjection\Kernel\KernelInterface;

class BundleInspector
{
    public function __construct(private KernelInterface $kernel)
    {
    }
}

This change is fully backward compatible: HttpKernel\Kernel now extends the new AbstractKernel, so existing applications keep working without any changes.

Opting Out of the Log Directory

HTTP-less applications often don't need the conventional var/log/ directory. In Symfony 8.1, getLogDir() is nullable on KernelTrait, so kernels can opt out by setting the APP_LOG_DIR environment variable to false:

1
2
# .env
APP_LOG_DIR=false

Deprecated HttpKernel Classes

Three classes have moved from HttpKernel to the DependencyInjection component. The original classes still work as backward-compatible aliases but are deprecated:

  • Symfony\Component\HttpKernel\Bundle\BundleInterfaceSymfony\Component\DependencyInjection\Kernel\BundleInterface
  • Symfony\Component\HttpKernel\DependencyInjection\MergeExtensionConfigurationPassSymfony\Component\DependencyInjection\Compiler\MergeExtensionConfigurationPass
  • Symfony\Component\HttpKernel\Config\FileLocatorSymfony\Component\DependencyInjection\Kernel\FileLocator

Building Simpler HTTP-Less Applications

Nicolas Grekas
Contributed by Nicolas Grekas in #63880

Another friction point of HTTP-less console applications is pulling in the full FrameworkBundle. In Symfony 8.1, its core has been split into two standalone bundles:

  • Symfony\Component\DependencyInjection\Kernel\ServicesBundle provides foundational DI services (event dispatcher, filesystem, clock, environment variable processors).
  • Symfony\Component\Console\ConsoleBundle provides console services (command registration, argument resolver, error listener).

A minimal console app only needs to register ConsoleBundle:

1
2
3
4
// config/bundles.php
return [
    Symfony\Component\Console\ConsoleBundle::class => ['all' => true],
];

In this example, ServicesBundle is loaded automatically as a dependency. This is now possible thanks to #[RequiredBundle], a new attribute to define dependency between bundles. You can use it in your own bundles too:

1
2
3
4
5
6
7
8
use Symfony\Component\DependencyInjection\Kernel\RequiredBundle;
use Symfony\Component\HttpKernel\Bundle\AbstractBundle;

#[RequiredBundle(AcmeCoreBundle::class)]
#[RequiredBundle(AcmeUtilBundle::class, ignoreOnInvalid: true)]
class AcmeBlogBundle extends AbstractBundle
{
}

The attribute is repeatable, resolves recursively (if A requires B and B requires C, all three are loaded in the right order) and accepts ignoreOnInvalid: true to silently skip optional dependencies that aren't installed.

Published in #Living on the edge