boatware/symfony-starter

A production-ready Symfony 8 starter kit — auth, admin, async messaging, Tailwind CSS, FrankenPHP and Bun.

Maintainers

Package info

codeberg.org/boatware3/symfony-starter

Type:project

pkg:composer/boatware/symfony-starter

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

dev-main 2026-03-16 00:56 UTC

This package is not auto-updated.

Last update: 2026-03-16 23:17:52 UTC


README

A production-ready Symfony 8 starter kit. Everything you need to ship a real application — authentication, admin panel, async messaging, and modern frontend tooling — pre-wired and ready for your domain.

composer create-project boatware/symfony-starter my-app
cd my-app && make setup
open http://localhost:9161

What's included

FeatureDetails
AuthenticationRegistration · Email verification · Login · Password reset & change — CSRF-protected, rate-limited
Admin panelUser management · Audit log · Disk space monitor · Async mail tester
SettingsPer-user account settings with secure verification-token flows for email & password changes
Async messagingSymfony Messenger with Doctrine transport (no extra broker needed)
EmailTemplated HTML emails (EN + DE) via Symfony Mailer; caught locally by Mailpit
i18nEnglish and German out of the box; add more locales in minutes
FrontendTailwind CSS 4 · Stimulus · Turbo · Webpack Encore · Bun
Code qualityPHPStan (max) · Psalm · PHP-CS-Fixer · PHPUnit / Pest
CI/CDGitHub Actions · GitLab CI · Jenkinsfile — all ready to go
RuntimeFrankenPHP (replaces nginx + php-fpm) · PostgreSQL 16

Quick start

Prerequisites

1. Create the project

composer create-project boatware/symfony-starter my-app
cd my-app

composer create-project automatically copies .env.dist.env and generates a fresh APP_SECRET.

2. Review .env

Open .env and adjust anything that needs to change (database name, port, mailer settings, …). Defaults work out of the box for local development.

3. Start the stack

make setup

This builds the Docker image, installs PHP and JS dependencies, builds frontend assets, runs database migrations, and seeds the database.

4. Open the app

http://localhost:9161
URLService
http://localhost:9161Application
http://localhost:8025Mailpit (caught emails)
http://localhost:8081Adminer (database UI)

5. Log in

EmailPasswordRole
admin@example.comadminAdmin
user@example.comuserUser

Change these passwords immediately on any non-local environment.

Development

Container commands

All PHP commands run inside the php container:

docker compose exec php bin/console <command>
docker compose exec php composer <command>
docker compose exec php bun run <script>

Make targets

make setup         # First-time setup
make start         # Start containers
make stop          # Stop containers
make destroy       # Remove containers (keep volumes)
make destroy-volumes  # Remove containers AND data volumes

make lint          # PHP syntax check
make phpcs         # Fix code style
make phpcs-dry     # Check code style (dry run)
make phpstan       # Static analysis (PHPStan)
make psalm         # Static analysis (Psalm)
make test          # Run full test suite
make qa            # All checks + tests

make cache         # Clear Symfony cache
make auto-migrate  # Generate + run DB migration
make reset-database  # Drop, recreate, migrate, seed

make generate-secret  # Regenerate APP_SECRET in .env
make start-messenger  # Start async worker manually

Asset pipeline

Assets are compiled with Webpack Encore, run by Bun (instead of npm):

docker compose exec php bun run dev     # Development build
docker compose exec php bun run watch   # Watch mode
docker compose exec php bun run build   # Production build

Database

The default database URL points to the PostgreSQL container.
For a local connection (e.g. from a GUI client), check the DB_PORT in .env (default 5432).

Sending emails

In development all emails are intercepted by Mailpit at http://localhost:8025. No emails leave your machine.

Architecture

Directory structure

src/
  Command/          CLI commands
  Controller/       HTTP layer — all extend BaseController
  DataFixtures/     Foundry factories + stories (groups: dev, test)
  Entity/           Doctrine ORM entities (final, UUID PKs)
  Enum/             PHP 8.1 enums (Role, LogLevel)
  EventSubscriber/  Locale detection, session debug, preview reset
  Exception/        EntityMismatchException
  Form/             FormTypes + DTOs
  Message/          Messenger message classes (async/sync)
  MessageHandler/   Corresponding handlers
  Monolog/          DoctrineLogHandler (writes to DB)
  Pagination/       Generic pagination helper
  PHPStan/          Custom PHPStan rules
  Repository/       Data access layer
  Security/         UserChecker
  Service/          Business logic (Mailer, ContentRenderer, DiskSpace, Validators)
  Twig/             AppExtension

Key conventions

Entitiesfinal, implement EntityInterface (toArray()), UUID primary key via PHP 8.4 asymmetric visibility (public private(set) Uuid $id).

Repositories — implement RepositoryInterface, expose save() / delete() (throw EntityMismatchException on wrong type).

Controllers — extend BaseController, use #[Route] attributes, #[IsGranted] for auth, #[CurrentUser] for injection.

Forms — edit flows use DTOs; Service/DTO/ services handle entity↔DTO conversion.

Translationstranslations/messages+intl-icu.{en,de}.yml for application messages, security+intl-icu.{en,de}.yml for authentication.

Content pages — static Markdown files in content/{locale}/ rendered via ContentRenderer (supports front-matter for template selection).

Custom PHPStan rules

Three rules are enforced in src/PHPStan/Rules/:

RuleEnforces
ControllerExtendsBaseControllerRuleAll controllers must extend BaseController
EntityMustBeFinalRuleAll entities must be final
RepositoryMustImplementInterfaceRuleAll repositories must implement RepositoryInterface

Configuration

Environment variables

VariableDefaultDescription
APP_ENVdevdev / prod / test
APP_SECRET(generated)Symfony secret — generated on create-project
APPLICATION_PORT9161Host port the app listens on
DATABASE_URL(see .env.dist)PostgreSQL connection string
MESSENGER_TRANSPORT_DSNdoctrine://default?...Messenger transport
MAILER_DSNsmtp://mailer:1025Mailer transport
MAILER_FROM_NAMESymfony StarterSender name
MAILER_FROM_ADDRESSnoreply@example.comSender address
TIMEZONEUTCApp timezone
IS_PREVIEW_SYSTEM0Show preview reset banner

Changing the app name

Update app.name in translations/messages+intl-icu.en.yml (and .de.yml). Also update MAILER_FROM_NAME in .env.

Adding locales

  1. Add the locale to app.locales in config/services.yaml
  2. Create translations/messages+intl-icu.{locale}.yml
  3. Create translations/security+intl-icu.{locale}.yml
  4. Create content/{locale}/ with imprint.md and privacy.md
  5. Add email templates under templates/emails/{locale}/

Testing

make test           # Full suite (prepare-test + pest)

Tests are split by type:

PathType
tests/Unit/Isolated, no DB
tests/Integration/Kernel-booted, wrapped in transactions (DAMA)
tests/Controller/WebTestCase HTTP tests
tests/Security/CSRF, rate-limiting, token security
tests/Browser/Browser simulation

Deployment

Production with Docker

make restart-prod

This rebuilds the production image, installs prod-only PHP/JS deps, runs migrations, and warms the cache.

Environment

Copy .env.dist to .env on the server and set:

APP_ENV=prod
APP_SECRET=<long-random-string>
DATABASE_URL=postgresql://...
MAILER_DSN=smtp://your-smtp-server

FrankenPHP HTTPS

To enable automatic TLS, change SERVER_NAME in your environment:

# Single domain
SERVER_NAME=example.com

# With www redirect
SERVER_NAME=example.com, www.example.com

FrankenPHP (via Caddy) will obtain and renew Let's Encrypt certificates automatically.

Contributing

See CONTRIBUTING.md.

License

MIT — see LICENSE.md.