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
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 \BundleInterface Symfony\Component \DependencyInjection \Kernel \BundleInterface Symfony→\Component \HttpKernel \DependencyInjection \MergeExtensionConfigurationPass Symfony\Component \DependencyInjection \Compiler \MergeExtensionConfigurationPass Symfony→\Component \HttpKernel \Config \FileLocator Symfony\Component \DependencyInjection \Kernel \FileLocator
Building Simpler HTTP-Less Applications
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:
Symfonyprovides foundational DI services (event dispatcher, filesystem, clock, environment variable processors).\Component \DependencyInjection \Kernel \ServicesBundle Symfony\Component\Console\ConsoleBundleprovides 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.
If you want to see this in action before 8.1 ships, the Symfony TUI Games is a working demo of a Symfony application without FrameworkBundle and without HTTP — just ConsoleBundle and a kernel built around KernelTrait. https://github.com/GromNaN/symfony-tui-games/
The perfect time to add a CLI firewall!
ConsoleBundle 💙 this is so great, thank you for splitting everything to achieve http less kernels, this will be super useful (at least for me who builds a lot of non http symfony apps)
Sounds not good if you need & use only di-container ...