Application email in Symfony moves through message-building code and the configured Mailer transport. A small console command that sends one welcome message proves the application can create an Email object, hand it to MailerInterface, and reach the selected transport before the same pattern is used in a controller, form handler, or background job.
MailerInterface is the application service that hands an Email object to the configured transport. The Mailer DSN decides where the message goes, so a local catcher such as Mailpit keeps development sends visible without reaching real recipients.
Projects created with the webapp recipe may route Mailer messages through Messenger. Keeping the local transport synchronous during the smoke test makes the message appear in the catcher immediately; production apps can switch back to an async transport and run a worker.
Related: How to configure Symfony Mailer
Related: How to create a Symfony console command
$ docker run --detach --name mailpit --publish 1025:1025 --publish 8025:8025 axllent/mailpit
Port 1025 accepts SMTP mail from Symfony. Port 8025 serves the Mailpit web UI and API for checking captured messages.
MAILER_DSN=smtp://127.0.0.1:1025 MESSENGER_TRANSPORT_DSN=sync://
Use MESSENGER_TRANSPORT_DSN=sync:// only for a local smoke test when the project routes Mailer messages through Messenger. Remove it or restore the async DSN before testing queue workers.
Related: How to configure Symfony Mailer
Related: How to configure a Symfony Messenger queue
<?php namespace App\Command; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Mailer\MailerInterface; use Symfony\Component\Mime\Email; #[AsCommand( name: 'app:send-welcome-email', description: 'Send a welcome email through Symfony Mailer.', )] final class SendWelcomeEmailCommand extends Command { public function __construct(private readonly MailerInterface $mailer) { parent::__construct(); } protected function configure(): void { $this->addArgument('recipient', InputArgument::REQUIRED, 'Recipient email address'); } protected function execute(InputInterface $input, OutputInterface $output): int { $recipient = (string) $input->getArgument('recipient'); $email = (new Email()) ->from('app@example.com') ->to($recipient) ->subject('Welcome to Example App') ->text('Your Symfony Mailer setup can now send application email.'); $this->mailer->send($email); $output->writeln(sprintf('Sent welcome email to %s', $recipient)); return Command::SUCCESS; } }
Move the same MailerInterface injection into a controller or service when the real application action should send the message.
Related: How to create a Symfony console command
$ php bin/console cache:clear // Clearing the cache for the dev environment with debug true [OK] Cache for the "dev" environment (debug=true) was successfully cleared.
Related: How to clear Symfony cache
$ php bin/console app:send-welcome-email reader@example.net Sent welcome email to reader@example.net
$ curl -sS http://127.0.0.1:8025/api/v1/messages
{
"total": 1,
"messages": [
{
"From": {"Address": "app@example.com"},
"To": [{"Address": "reader@example.net"}],
"Subject": "Welcome to Example App",
"Snippet": "Your Symfony Mailer setup can now send application email."
}
]
}
The same message should also appear in the Mailpit inbox at http://127.0.0.1:8025.
$ docker rm --force mailpit mailpit