LangV AWS CDK and chat migration
Budget: $250 – $750 USD
We have an existing Laravel 9 application (PHP 8, Vue 3, Vite, MySQL) currently running on a traditional single-server LAMP stack. We need to migrate it to AWS using CDK and replace Firebase Realtime Database with Laravel Reverb for chat functionality.
We will provide a fresh AWS account with ACM certificate already verified via Cloudflare.
**Scope of Work**
1. AWS CDK Infrastructure Setup
- Write AWS CDK stack (TypeScript) to provision:
- VPC with public and private subnets
- Application Load Balancer (ALB) with HTTPS and WebSocket support (idle timeout 3600s)
- EC2 instance (private subnet) running PHP 8, Apache, Supervisor
- RDS MySQL from local (private subnet, accessible only from EC2)
- ElastiCache Redis (private subnet, accessible only from EC2)
- S3 bucket for media file storage
- Configure ALB target group with sticky sessions for WebSocket connections
- All secrets and configuration values stored in AWS Secrets Manager and SSM Parameter Store (no hardcoded credentials)
- IAM role for EC2 with permissions to read from Secrets Manager, SSM, access S3, and SSM Session Manager access
- EC2 must have SSM agent installed and enabled for ec2ic terminal access
2. EC2 Boot Scripts and Secrets Management
- All application environment variables must be stored in AWS Secrets Manager or SSM Parameter Store via CDK, including:
- Database credentials (DB_HOST, DB_PORT, DB_DATABASE, DB_USERNAME, DB_PASSWORD)
- Redis connection details (REDIS_HOST, REDIS_PORT, REDIS_PASSWORD)
- S3 bucket configuration (AWS_BUCKET, AWS_DEFAULT_REGION)
- Application secrets (APP_KEY, JWT secrets, API keys)
- GitHub deploy key for repository access
- user-data.sh (runs on first boot only):
- Installs PHP 8, Apache, Composer, Node.js, Supervisor, AWS CLI, and CloudWatch agent
- Configures Apache virtual host pointing to /opt/app/public
- Installs Supervisor config for Reverb and queue worker
- init.sh (runs on every boot, installed in /var/lib/cloud/scripts/per-boot/):
- Wait for SSM/Secrets: Retries every 10 seconds for up to 10 minutes until RDS, Redis, S3, and all secrets are confirmed available and reachable
- Clone/pull the repo: Uses a read-only GitHub deploy key (retrieved from Secrets Manager) via SSH to git clone on first boot or git pull origin main on subsequent boots into /opt/app
- Install dependencies: Runs install and build
- Write .env: Assembles all secrets and parameters from Secrets Manager and SSM Parameter Store (DB creds, JWT, Redis auth, API keys) into /opt/app/.env
- Start services: Restarts Apache and Supervisor (which manages Reverb and queue worker)
- Note: init.sh does NOT run any database commands — migrations and seeding are handled separately by a human via SSM terminal
3. RDS MySQL Database Initialization (Human-Run via SSM Terminal)
- After CDK deploy and EC2 boot, a human connects to the EC2 via SSM Session Manager (aws ssm start-session --target i-xxxxx) and runs migrations directly on the EC2, where PHP, Laravel, .env, and RDS network access are all already available
- Provide a helper script in the EC2 (scripts/db-init.sh) for convenience with two modes:
- First-time setup: Runs php artisan migrate then php artisan db:seed --class=ProductionSeeder
- Subsequent deploys: Runs php artisan migrate --force only
- Refactor the existing database initialization approach:
- Currently, some migrations contain inline DB::table()->insert() calls that mix seed data with schema creation (e.g., categories, tags, and settings migrations insert data directly). This must be separated.
- Move all inline seed data out of migration files — migrations should only define schema (create/alter tables)
- Create a ProductionSeeder for essential data that must exist in production: roles, countries, default categories (Teacher, Translator, Interpreter), default settings, admin user
- Create a DemoSeeder for development/staging only: dummy users, sample sellers, sample gigs (never run in production)
- Enable MYSQL_ATTR_SSL_CA for encrypted connections between EC2 and RDS
- RDS credentials managed entirely via Secrets Manager (auto-generated by CDK, already written to .env by init.sh)
4. Replace Firebase Realtime Database with Laravel Reverb
- Remove kreait/firebase-php dependency
- Remove Firebase service account key file from repository and git history
- Refactor ChatController.php and Seller/ChatController.php to store chat messages in MySQL (RDS)
- Install and configure Laravel Reverb as the WebSocket server
- Set up Laravel Echo on the Vue 3 frontend for real-time message delivery
- Configure Reverb to use Redis (ElastiCache) as the pub/sub backend
5. Switch Laravel Services to Redis (ElastiCache)
- Change CACHE_DRIVER from file to redis
- Change SESSION_DRIVER from file to redis
- Change QUEUE_CONNECTION from sync to redis
- Change BROADCAST_DRIVER from log to reverb
- Ensure all existing queued jobs (e.g., Spatie Media Library conversions) run via Redis queue worker
6. Migrate File Storage to S3
- Change FILESYSTEM_DISK from local to s3
- Configure Spatie Media Library to use the S3 disk
- Migrate existing media files from public/media to S3 bucket
- Update any hardcoded local file paths in the application
7. EC2 Process Management (Supervisor)
- Configure Supervisor to manage two processes:
- php artisan reverb:start (WebSocket server)
- php artisan queue:work redis (background job worker)
- Apache managed separately via systemd
**Current Tech Stack**
- Backend: PHP 8 / Laravel 9
- Frontend: Vue 3, Vite, Bootstrap 5
- Database: MySQL (currently local, moving to RDS)
- Chat: Firebase Realtime Database (to be replaced)
- File storage: Local filesystem (to be replaced with S3)
- Cache/Sessions: File-based (to be replaced with Redis)
- Queue: Sync (to be replaced with Redis)
- Web server: Apache
**Deliverables**
- AWS CDK stack code, deployable and tested, with all secrets provisioned in Secrets Manager/SSM
- EC2 boot scripts (user-data.sh and init.sh) tested and working
- Helper script (scripts/db-init.sh) on EC2 for human-run database migrations and seeding
- Refactored migrations (schema only) and new ProductionSeeder / DemoSeeder
- Supervisor configuration files for Reverb and queue worker
- Apache virtual host configuration
- Updated Laravel application with Firebase removed and Reverb integrated
- Working real-time chat over WebSockets
- All services (cache, sessions, queue, broadcast, storage) running on AWS managed services
- Updated .env.example reflecting all new environment variables (values reference SSM/Secrets Manager, not hardcoded)
- Migration script or instructions for moving existing media files to S3
- Documentation for deployment, boot script flow, SSM access, db-init usage, secrets rotation, and infrastructure teardown
We will provide a fresh AWS account with ACM certificate already verified via Cloudflare.
**Scope of Work**
1. AWS CDK Infrastructure Setup
- Write AWS CDK stack (TypeScript) to provision:
- VPC with public and private subnets
- Application Load Balancer (ALB) with HTTPS and WebSocket support (idle timeout 3600s)
- EC2 instance (private subnet) running PHP 8, Apache, Supervisor
- RDS MySQL from local (private subnet, accessible only from EC2)
- ElastiCache Redis (private subnet, accessible only from EC2)
- S3 bucket for media file storage
- Configure ALB target group with sticky sessions for WebSocket connections
- All secrets and configuration values stored in AWS Secrets Manager and SSM Parameter Store (no hardcoded credentials)
- IAM role for EC2 with permissions to read from Secrets Manager, SSM, access S3, and SSM Session Manager access
- EC2 must have SSM agent installed and enabled for ec2ic terminal access
2. EC2 Boot Scripts and Secrets Management
- All application environment variables must be stored in AWS Secrets Manager or SSM Parameter Store via CDK, including:
- Database credentials (DB_HOST, DB_PORT, DB_DATABASE, DB_USERNAME, DB_PASSWORD)
- Redis connection details (REDIS_HOST, REDIS_PORT, REDIS_PASSWORD)
- S3 bucket configuration (AWS_BUCKET, AWS_DEFAULT_REGION)
- Application secrets (APP_KEY, JWT secrets, API keys)
- GitHub deploy key for repository access
- user-data.sh (runs on first boot only):
- Installs PHP 8, Apache, Composer, Node.js, Supervisor, AWS CLI, and CloudWatch agent
- Configures Apache virtual host pointing to /opt/app/public
- Installs Supervisor config for Reverb and queue worker
- init.sh (runs on every boot, installed in /var/lib/cloud/scripts/per-boot/):
- Wait for SSM/Secrets: Retries every 10 seconds for up to 10 minutes until RDS, Redis, S3, and all secrets are confirmed available and reachable
- Clone/pull the repo: Uses a read-only GitHub deploy key (retrieved from Secrets Manager) via SSH to git clone on first boot or git pull origin main on subsequent boots into /opt/app
- Install dependencies: Runs install and build
- Write .env: Assembles all secrets and parameters from Secrets Manager and SSM Parameter Store (DB creds, JWT, Redis auth, API keys) into /opt/app/.env
- Start services: Restarts Apache and Supervisor (which manages Reverb and queue worker)
- Note: init.sh does NOT run any database commands — migrations and seeding are handled separately by a human via SSM terminal
3. RDS MySQL Database Initialization (Human-Run via SSM Terminal)
- After CDK deploy and EC2 boot, a human connects to the EC2 via SSM Session Manager (aws ssm start-session --target i-xxxxx) and runs migrations directly on the EC2, where PHP, Laravel, .env, and RDS network access are all already available
- Provide a helper script in the EC2 (scripts/db-init.sh) for convenience with two modes:
- First-time setup: Runs php artisan migrate then php artisan db:seed --class=ProductionSeeder
- Subsequent deploys: Runs php artisan migrate --force only
- Refactor the existing database initialization approach:
- Currently, some migrations contain inline DB::table()->insert() calls that mix seed data with schema creation (e.g., categories, tags, and settings migrations insert data directly). This must be separated.
- Move all inline seed data out of migration files — migrations should only define schema (create/alter tables)
- Create a ProductionSeeder for essential data that must exist in production: roles, countries, default categories (Teacher, Translator, Interpreter), default settings, admin user
- Create a DemoSeeder for development/staging only: dummy users, sample sellers, sample gigs (never run in production)
- Enable MYSQL_ATTR_SSL_CA for encrypted connections between EC2 and RDS
- RDS credentials managed entirely via Secrets Manager (auto-generated by CDK, already written to .env by init.sh)
4. Replace Firebase Realtime Database with Laravel Reverb
- Remove kreait/firebase-php dependency
- Remove Firebase service account key file from repository and git history
- Refactor ChatController.php and Seller/ChatController.php to store chat messages in MySQL (RDS)
- Install and configure Laravel Reverb as the WebSocket server
- Set up Laravel Echo on the Vue 3 frontend for real-time message delivery
- Configure Reverb to use Redis (ElastiCache) as the pub/sub backend
5. Switch Laravel Services to Redis (ElastiCache)
- Change CACHE_DRIVER from file to redis
- Change SESSION_DRIVER from file to redis
- Change QUEUE_CONNECTION from sync to redis
- Change BROADCAST_DRIVER from log to reverb
- Ensure all existing queued jobs (e.g., Spatie Media Library conversions) run via Redis queue worker
6. Migrate File Storage to S3
- Change FILESYSTEM_DISK from local to s3
- Configure Spatie Media Library to use the S3 disk
- Migrate existing media files from public/media to S3 bucket
- Update any hardcoded local file paths in the application
7. EC2 Process Management (Supervisor)
- Configure Supervisor to manage two processes:
- php artisan reverb:start (WebSocket server)
- php artisan queue:work redis (background job worker)
- Apache managed separately via systemd
**Current Tech Stack**
- Backend: PHP 8 / Laravel 9
- Frontend: Vue 3, Vite, Bootstrap 5
- Database: MySQL (currently local, moving to RDS)
- Chat: Firebase Realtime Database (to be replaced)
- File storage: Local filesystem (to be replaced with S3)
- Cache/Sessions: File-based (to be replaced with Redis)
- Queue: Sync (to be replaced with Redis)
- Web server: Apache
**Deliverables**
- AWS CDK stack code, deployable and tested, with all secrets provisioned in Secrets Manager/SSM
- EC2 boot scripts (user-data.sh and init.sh) tested and working
- Helper script (scripts/db-init.sh) on EC2 for human-run database migrations and seeding
- Refactored migrations (schema only) and new ProductionSeeder / DemoSeeder
- Supervisor configuration files for Reverb and queue worker
- Apache virtual host configuration
- Updated Laravel application with Firebase removed and Reverb integrated
- Working real-time chat over WebSockets
- All services (cache, sessions, queue, broadcast, storage) running on AWS managed services
- Updated .env.example reflecting all new environment variables (values reference SSM/Secrets Manager, not hardcoded)
- Migration script or instructions for moving existing media files to S3
- Documentation for deployment, boot script flow, SSM access, db-init usage, secrets rotation, and infrastructure teardown