A complete newsletter management system for Laravel, built with Filament. Manage subscribers, campaigns, HTML templates, scheduled sending, and full tracking—all from a modern admin panel.
- Features
- Requirements
- Installation
- Configuration
- Quick Start
- Sending Newsletters
- Monitoring & Analytics
- Rate Limiting
- Public Routes
- Scheduled Tasks
- Artisan Commands
- Troubleshooting
| Feature | Description |
|---|---|
| Subscriber Management | Import/export CSV, tagging, status management |
| Campaigns & Messages | Hierarchical organization of campaigns and messages |
| HTML Templates | Customizable templates with placeholder support |
| Scheduled Sending | Automatic delivery via cron |
| Full Tracking | Opens, clicks, and unsubscribe tracking |
| Targeting | Filter recipients by tags and status |
| Dashboard | Statistics and monitoring widgets |
| Rate Limiting | Configurable per-minute, per-hour, and per-day limits |
| Bounce Detection | IMAP integration for bounce processing |
- PHP 8.2+
- Laravel 12
- Filament 5
- Database (SQLite, MySQL, or PostgreSQL)
- Queue driver (database, Redis, etc.)
- Clone the repository and install dependencies:
git clone <repository-url> newsletter
cd newsletter
composer install- Configure the environment:
cp .env.example .env
php artisan key:generate- Run migrations:
php artisan migrate- Build frontend assets:
npm install
npm run build- Create an admin user (if not using seed data):
php artisan make:filament-userConfigure your SMTP settings in .env:
MAIL_MAILER=smtp
MAIL_HOST=your-smtp-host.com
MAIL_PORT=587
MAIL_USERNAME=your-username
MAIL_PASSWORD=your-password
MAIL_FROM_ADDRESS="newsletter@yourdomain.com"
MAIL_FROM_NAME="${APP_NAME}"| Variable | Description | Default |
|---|---|---|
NEWSLETTER_TRACKING_ENABLED |
Enable open/click tracking | true |
NEWSLETTER_RATE_LIMIT_PER_MINUTE |
Max emails per minute (0 = unlimited) | 0 |
NEWSLETTER_RATE_LIMIT_PER_HOUR |
Max emails per hour (0 = unlimited) | 0 |
NEWSLETTER_RATE_LIMIT_PER_DAY |
Max emails per day (0 = unlimited) | 0 |
Optional configuration for processing bounced emails:
NEWSLETTER_IMAP_HOST=imap.yourdomain.com
NEWSLETTER_IMAP_PORT=993
NEWSLETTER_IMAP_USERNAME=your-username
NEWSLETTER_IMAP_PASSWORD=your-password
NEWSLETTER_IMAP_ENCRYPTION=ssl
NEWSLETTER_IMAP_FOLDER=INBOX- Seed sample data (optional, recommended for testing):
php artisan newsletter:seed-data- Start the queue worker:
./start-worker.shOr manually:
php artisan queue:work --tries=3 --timeout=90- Access the admin panel:
- URL:
https://newsletter.test(Laravel Herd) orhttp://localhost:8000 - Default credentials (after seeding):
admin@newsletter.test/password
- Go to Newsletter > Messages
- Create a new message or select an existing one
- Set status to Ready
- Click Send Now
- Emails are queued and sent automatically via the queue worker
- Create or edit a message
- Set the Scheduled Date field
- The system sends automatically at the scheduled time (requires cron—see Scheduled Tasks)
- Dashboard: Main KPIs and send statistics
- Messages: Status and send counts per message
- Message Details: Individual tracking (opens, clicks) per recipient
Configure sending limits to comply with your SMTP provider's constraints:
NEWSLETTER_RATE_LIMIT_PER_MINUTE=60
NEWSLETTER_RATE_LIMIT_PER_HOUR=1000
NEWSLETTER_RATE_LIMIT_PER_DAY=10000Limits are progressive: daily overrides hourly, hourly overrides per-minute.
Check current rate limits:
php artisan newsletter:rate-limitsThe following routes are available for public use:
| Route | Method | Description |
|---|---|---|
/subscribe |
GET | Subscription form |
/subscribe |
POST | Process subscription |
/subscribe/confirm/{token} |
GET | Confirm subscription (double opt-in) |
/unsubscribe/{subscriber} |
GET | Unsubscribe form |
/unsubscribe/{subscriber}/confirm |
POST | Confirm unsubscribe |
Add the Laravel scheduler to your crontab:
* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1| Task | Schedule | Description |
|---|---|---|
newsletter:send-scheduled |
Every minute | Sends messages with scheduled date in the past |
newsletter:process-bounces |
Every 15 minutes | Processes bounced emails via IMAP |
backup:run |
Daily at 03:00 | Runs application backup |
backup:clean |
Daily at 04:00 | Cleans old backups |
| Command | Description |
|---|---|
newsletter:seed-data |
Populate database with sample subscribers, campaigns, and messages |
newsletter:send-scheduled |
Manually trigger scheduled message sending |
newsletter:process-pending |
Process pending emails in the queue |
newsletter:process-bounces |
Process bounced emails from IMAP |
newsletter:rate-limits |
Display current rate limit status |
Once your app is ready, deploy it to any Ubuntu VPS with cipi.sh — an open-source CLI built exclusively for Laravel.
- Full app isolation — own Linux user, PHP-FPM pool & database per app
- Zero-downtime deploys with instant rollback via Deployer
- Let's Encrypt SSL, Fail2ban, UFW firewall — all automated
- Multi-PHP (7.4 → 8.5), queue workers, S3 backups, auto-deploy webhooks
- AI Agent ready — any AI with SSH access can deploy & manage your server
wget -O - https://raw.githubusercontent.com/andreapollastri/cipi/refs/heads/latest/install.sh | bash→ Learn more at cipi.sh
- Ensure the queue worker is running:
./start-worker.shorphp artisan queue:work - Manually process pending:
php artisan newsletter:process-pending - Check the jobs table:
php artisan tinker --execute="DB::table('jobs')->count()"
php artisan tinker --execute="Mail::raw('Test', fn(\$m) => \$m->to('test@example.com'))"php artisan newsletter:rate-limitsRun npm run build or npm run dev to compile assets.
MIT
Created by Andrea Pollastri