This project serves as the Mumzworld Dockerized starter kit for building Laravel-based services. It comes pre-configured with a comprehensive Docker environment to support local development and provides a solid foundation for building robust API and worker services.
The aim is to provide a quick and consistent setup for Mumzworld developers, including common services like MySQL, Redis, DynamoDB (local), and Horizon for background jobs, along with useful development tools.
This starter kit includes a multi-container Docker setup managed by docker-compose.yml. The environment is configurable via a .env file (copy .env.example to .env to get started).
app: The main Laravel application container running with FrankenPHP.- Serves the API.
- Runs Artisan commands.
- Configurable for development (
Dockerfile.devwith Xdebug) and production (Dockerfile.prod).
horizon: A dedicated container for running Laravel Horizon queue workers, also using FrankenPHP.- Configurable for development (
Dockerfile.dev) and production (Dockerfile.prod).
- Configurable for development (
mysql: MySQL database service.redis: Redis in-memory data store (for caching, sessions, queues).dynamodb: AWS DynamoDB Local instance for development purposes.otel-collector: OpenTelemetry Collector for receiving and processing traces.tempo: Grafana Tempo for storing and querying distributed traces.grafana: Grafana dashboard for visualizing traces and monitoring.phpmyadmin: Web UI for managing the MySQL database.redis-commander: Web UI for managing the Redis data store.dynamodb-admin: Web UI for managing the local DynamoDB data.
Once the containers are running, you can access the following admin interfaces:
- Main Application: http://localhost:80 (default port, configurable via
DOCKER_APP_HTTP_PORT) - Grafana Dashboard: http://localhost:3001 (admin/admin) - for viewing distributed traces
- phpMyAdmin: http://localhost:8080 (configurable via
DOCKER_PHPMYADMIN_HOST_PORT) - Redis Commander: http://localhost:8081 (configurable via
DOCKER_REDIS_COMMANDER_HOST_PORT) - DynamoDB Admin: http://localhost:8001 (configurable via
DOCKER_DYNAMODB_ADMIN_HOST_PORT) - Laravel Horizon: http://localhost:80/horizon (requires setting up Horizon routes)
- Ensure Docker Desktop (or Docker Engine with Compose V2) is installed and running.
- Copy
.env.exampleto.envand customize as needed:The OpenTelemetry configuration is pre-configured but can be customized:cp .env.example .env
OTEL_SERVICE_NAME=laravel-starter-kit-service OTEL_TRACES_EXPORTER=otlp OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4318 OTEL_PROPAGATORS=baggage,tracecontext OTEL_PHP_AUTOLOAD_ENABLED=true
- Build and start the containers:
docker compose up --build -d
- Generate the Laravel application key:
docker compose exec app php artisan key:generate - Run database migrations:
# Run standard Laravel migrations for MySQL docker compose exec app php artisan migrate # Run DynamoDB migrations docker compose exec app php artisan migrate:dynamodb
Your Laravel API should now be accessible (typically at http://localhost or the port you configured for DOCKER_APP_HTTP_PORT). The admin UIs for databases will be on their respective configured ports.
Refer to docs/infrastructure.md for more detailed information on the Docker setup and environment variables.
This starter kit is configured to output all logs in JSON format to stdout, making it compatible with container orchestration platforms and centralized logging systems.
- JSON-formatted logs for structured parsing
- All application logs sent to stdout
- Both Laravel and PHP native errors captured in the same format
- Log configuration for both main application and Horizon workers
The PHP configuration is set up to ensure all errors are properly logged:
log_errors = On
error_log = /dev/stdout
display_errors = Off
display_startup_errors = Off
html_errors = Off
error_reporting = E_ALLThe starter kit includes a Debug Controller that helps test error handling, logging configurations, and monitoring integrations.
The Debug Controller (app/Http/Controllers/DebugController.php) provides endpoints to trigger various types of errors and exceptions:
- /debug: Triggers all error types at once in a "chaos error" scenario
- /debug/division-by-zero: Triggers a division by zero error
- /debug/undefined-variable: Triggers an undefined variable error
- /debug/type-error: Triggers a type error
- /debug/out-of-bounds: Triggers an array out of bounds error
- /debug/logic-exception: Throws a LogicException
- /debug/runtime-exception: Throws a RuntimeException
- /debug/query-exception: Triggers a database query exception
- /debug/http-exception: Throws an HTTP exception
- /debug/memory-limit: Simulates hitting memory limit
- /debug/parse-error-example: Shows example of parse error
- /debug/fatal-error: Triggers a fatal error
- /debug/custom-exception: Throws a custom exception
- /debug/random-error: Randomly triggers one of the above errors
Use these endpoints to:
- Test your logging configuration
- Verify error capture in monitoring systems
- Check exception handling middleware
- Ensure errors are properly formatted in JSON
Warning: These endpoints intentionally trigger errors and should only be used in development/testing environments.
This starter kit includes a comprehensive test suite with PHPUnit, covering unit tests, feature tests, and API tests.
# Run all tests via Laravel's artisan command
composer test
# Run all tests via PHPUnit directly (suppresses OpenTelemetry warnings)
composer test:phpunit
# Run only unit tests
composer test:unit
# Run only feature tests
composer test:featureTo generate code coverage reports, you need either Xdebug or PCOV PHP extension installed.
# Generate coverage with PCOV (recommended - faster)
composer test:coverage:pcov
# Generate coverage with Xdebug
composer test:coverage
# View HTML report in browser
open coverage/html/index.htmlCoverage reports are generated in the coverage/ directory:
coverage/html/index.html- HTML report (open in browser for detailed view)coverage/clover.xml- Clover XML format (for CI/CD integration)
PCOV is faster than Xdebug for code coverage. To install on macOS:
# Install pcre2 dependency
brew install pcre2
# Build and install PCOV
cd /tmp && rm -rf pcov-build && mkdir pcov-build && cd pcov-build
curl -L https://pecl.php.net/get/pcov-1.0.12.tgz | tar xz
cd pcov-1.0.12
phpize
./configure CFLAGS="-I$(brew --prefix pcre2)/include"
make
# Copy to PHP extension directory
PHP_EXT_DIR=$(php -r "echo ini_get('extension_dir');")
mkdir -p "$PHP_EXT_DIR"
cp modules/pcov.so "$PHP_EXT_DIR/"
# Enable the extension
PHP_INI_DIR=$(php -r "echo PHP_CONFIG_FILE_SCAN_DIR;")
echo "extension=pcov.so" > "$PHP_INI_DIR/ext-pcov.ini"
# Verify installation
php -m | grep pcovpecl install xdebug
# Add to php.ini
echo "zend_extension=xdebug.so" >> $(php -r "echo php_ini_loaded_file();")
echo "xdebug.mode=coverage" >> $(php -r "echo php_ini_loaded_file();")tests/
├── TestCase.php # Base test class
├── Unit/
│ └── Models/
│ └── ExampleModelDynamoDBTest.php # DynamoDB model tests
└── Feature/
├── HomePageTest.php # Home page tests
├── Api/
│ └── HealthCheckTest.php # API health endpoint tests
├── Http/
│ └── Controllers/
│ └── DebugControllerTest.php # Debug endpoints tests
└── Console/
└── Commands/
├── DynamoDbMigrateTest.php # Migration command tests
├── MakeDynamoDbMigrationTest.php # Migration generator tests
└── MakeDynamoDbModelTest.php # Model generator tests
| Category | Description | Command |
|---|---|---|
| Unit Tests | Test individual classes in isolation | composer test:unit |
| Feature Tests | Test HTTP endpoints and full request lifecycle | composer test:feature |
| API Tests | Test API endpoints (under Feature/Api) | composer test:feature |
| Command Tests | Test Artisan commands (under Feature/Console) | composer test:feature |
# Run tests inside the app container
docker compose exec app composer test
# Run with coverage (requires Xdebug in Dockerfile.dev)
docker compose exec app composer test:coverageUnit Test Example:
namespace Tests\Unit;
use Tests\TestCase;
use App\Models\YourModel;
class YourModelTest extends TestCase
{
public function test_model_has_correct_fillable(): void
{
$model = new YourModel();
$this->assertEquals(['field1', 'field2'], $model->getFillable());
}
}Feature Test Example:
namespace Tests\Feature;
use Tests\TestCase;
class YourEndpointTest extends TestCase
{
public function test_endpoint_returns_success(): void
{
$response = $this->getJson('/api/v1/your-endpoint');
$response->assertStatus(200)
->assertJsonStructure(['data', 'message']);
}
}This starter kit includes a GitHub Actions workflow for automated test coverage on pull requests.
A workflow is configured at .github/workflows/test-coverage.yml that:
- Triggers when the
reviewlabel is added to a PR - Runs the full test suite with PCOV coverage
- Posts a coverage report as a comment on the PR
To use:
- Create a PR with your changes
- Add the
reviewlabel to the PR - The workflow runs and posts coverage results as a comment
The comment includes:
- Coverage summary (Lines, Methods, Classes percentages)
- Expandable full coverage report
- Link to the GitHub Actions run
# Example GitHub Actions step for custom workflows
- name: Setup PHP with PCOV
uses: shivammathur/setup-php@v2
with:
php-version: '8.2'
extensions: mbstring, xml, pcov
coverage: pcov
- name: Install dependencies
run: composer install --ignore-platform-req=ext-opentelemetry
- name: Run Tests
run: composer test:phpunit
# With coverage
- name: Run Tests with Coverage
run: php -d pcov.enabled=1 vendor/bin/phpunit --coverage-clover coverage/clover.xml
- name: Upload Coverage to Codecov
uses: codecov/codecov-action@v4
with:
files: coverage/clover.xml- OpenTelemetry Warning: When running tests locally without the OpenTelemetry PHP extension, you may see a warning. This is expected and doesn't affect test execution.
- Database: Tests use SQLite in-memory database by default (configured in
phpunit.xml). - Environment: Tests run with
APP_ENV=testingand various services disabled (Telescope, Pulse, etc.).
This starter kit includes AWS DynamoDB Local for development, along with the necessary tools to create models and run migrations.
DynamoDB table names can be configured via environment variables to support different table names across environments (staging, production, etc.).
Configuration:
Add table name variables to your .env file:
DYNAMODB_EXAMPLE_TABLE=example_model_dynamodbThe starter kit includes an example model that demonstrates this pattern. When creating your own models, follow this approach:
- Add to
config/app.php:
'dynamodb_your_table' => env('DYNAMODB_YOUR_TABLE', 'default_table_name'),- Update your model constructor:
public function __construct(array $attributes = [])
{
$this->table = config('app.dynamodb_your_table', 'default_table_name');
parent::__construct($attributes);
}- Update migrations:
$tableName = config('app.dynamodb_your_table', 'default_table_name');This allows you to use different table names per environment:
- Development:
dev_your_table - Staging:
staging_your_table - Production:
prod_your_table
To create a new DynamoDB model class:
php artisan make:dynamodb-model YourModelNameThis will generate a model file in app/Models/YourModelName.php that extends BaoPham\DynamoDb\DynamoDbModel. The model will include:
- Table name configuration
- Primary key definition
- Attribute definitions and casting
- Examples of accessor and mutator methods
- Index configuration options
Example model structure:
class YourModelName extends DynamoDbModel
{
protected $table = 'your_model_table_name';
protected $primaryKey = 'id';
protected $keyType = 'string';
public $incrementing = false;
protected $fillable = [
'id',
'name',
// other attributes
];
// Global Secondary Indexes
protected $dynamoDbIndexKeys = [
'name-index' => [
'hash' => 'name',
]
];
// ... additional model methods
}To create a DynamoDB migration:
php artisan make:dynamodb-migration create_your_table_nameThis will generate a migration file in database/migrations_dynamodb/ with methods for creating and deleting a DynamoDB table:
Example migration structure:
public function up(): void
{
$client = app(DynamoDbClientService::class)->getClient();
$tableName = 'your_table_name';
$client->createTable([
'TableName' => $tableName,
'AttributeDefinitions' => [
[
'AttributeName' => 'id',
'AttributeType' => 'S'
],
// Define attributes used in key schema or indexes
],
'KeySchema' => [
[
'AttributeName' => 'id',
'KeyType' => 'HASH'
]
],
'GlobalSecondaryIndexes' => [
// Define any GSIs
],
'BillingMode' => 'PAY_PER_REQUEST',
// Other table settings
]);
// Wait until the table is created
$client->waitUntil('TableExists', [
'TableName' => $tableName
]);
}To run all DynamoDB migrations:
php artisan migrate:dynamodbTo run a specific migration:
php artisan migrate:dynamodb --file=YYYY_MM_DD_HHMMSS_create_your_table_name.phpTo drop all tables and re-create them:
php artisan migrate:dynamodb --fresh- AttributeDefinitions: Only include attributes that are used in the table's key schema or indexes.
- Local Development Credentials: DynamoDB Local uses hardcoded credentials:
- Access Key:
dynamodblocal - Secret Key:
secret
- Access Key:
- DYNAMODB_CONNECTION: Set to
localin your.envfor development with DynamoDB Local. - Endpoint: For local development, the endpoint is configured as
http://dynamodb:8000(Docker service name).
For more details on working with DynamoDB in Laravel, refer to the baopham/laravel-dynamodb documentation.
This starter kit includes comprehensive OpenTelemetry integration using the mumzworld/laravel-opentelemetry package for distributed tracing and observability.
- ✅ Automatic Tracing: HTTP requests, DynamoDB operations, cache operations
- ✅ Custom Tracing: TracerService for business logic tracing
- ✅ Complete Observability Stack: Collector, Tempo, Grafana
- ✅ Production Ready: Configurable sampling and performance optimizations
- Start services with
docker compose up --build -d - Access Grafana at http://localhost:3001 (admin/admin)
- Test tracing with built-in endpoints:
# Test OpenTelemetry functionality
curl http://localhost/api/opentelemetry/test
# View configuration
curl http://localhost/api/opentelemetry/config
# Test nested spans
curl http://localhost/api/opentelemetry/nesteduse Mumzworld\LaravelOpenTelemetry\Services\TracerService;
class UserService
{
public function __construct(private TracerService $tracer) {}
public function createUser(array $data): User
{
return $this->tracer->trace('user.create', function() use ($data) {
return User::create($data);
}, [
'user.email' => $data['email'],
'user.type' => $data['type'] ?? 'regular'
]);
}
}- Open Grafana: http://localhost:3001
- Navigate: Explore → Tempo
- Query traces:
{service.name="laravel-starter-kit-service"}
Note: The
mumzworld/laravel-opentelemetrypackage is now public and requires no authentication.
For comprehensive setup instructions, configuration options, and troubleshooting, see docs/OPENTELEMETRY_SETUP.md.
This starter kit is designed as a microservice template with the following architecture:
- DynamoDB: Primary data storage (configured via environment variables)
- Redis: Caching, sessions, and queue management
- MySQL: Available but optional (not required for DynamoDB-only services)
This starter kit does not include user authentication by default, as it's designed for microservices that:
- Receive user context from external systems (API gateways, auth services)
- Don't manage user sessions directly
- Focus on business logic rather than authentication
If your service requires authentication, you can add Laravel Sanctum or Passport as needed.
The following Laravel defaults have been removed to keep the starter kit lean:
- MySQL user migrations (users, password_resets tables)
- User model and factory
- Sanctum authentication routes
- Default database seeders
This keeps the focus on DynamoDB-based microservices while maintaining the flexibility to add these components back if needed.