Deploy Laravel to Production with Temps
Deploy Laravel with Temps: git-push deploys, automatic HTTPS via Pingora, managed PostgreSQL and Redis, Artisan migrations on every deploy, and built-in monitoring.
Temps Team
June 6, 2026 · 2mo ago
Deploying Laravel to production without shared hosting means you get a reliable server with full control over your stack — but setting up Nginx, PHP-FPM, SSL, queue workers, and scheduled tasks by hand takes hours. Temps collapses that entire stack into a single Rust binary you control: git-push to deploy, automatic HTTPS via Pingora (Cloudflare's open-source proxy), managed PostgreSQL and Redis, Artisan migrations that run on every deploy, and built-in monitoring — free to self-host on the server you already pay for, with a Temps Cloud managed add-on for telemetry retention, offsite backups, and AI credits coming soon.
TL;DR:
bunx @temps-sdk/cli deploy my-laravel-app -b main -e production -y— your Laravel app is live with HTTPS, managed database, queue workers, and health-checked deployments in under 5 minutes. Temps is Apache 2.0, self-hostable for free, with a Temps Cloud managed add-on (telemetry retention, offsite backups, AI credits) coming soon.
How Do I Deploy Laravel to Production Without Laravel Forge?
The fastest path: push your code to a git repository, connect it to Temps, and run one CLI command. Temps auto-detects PHP projects, configures Nginx + PHP-FPM, installs Composer dependencies, runs your Artisan migrations, and starts routing encrypted traffic — all without touching a single config file.
What You'll Get
After this tutorial, your Laravel app will have:
- Nginx + PHP-FPM production stack — the same setup Laravel recommends, configured automatically
- Artisan migrations on every deploy —
php artisan migrate --forceruns before traffic switches over - Managed PostgreSQL with automatic backups and HA support
- Managed Redis for caching, sessions, and queues
- Queue workers and scheduler — Laravel Horizon supported
- Auto-rollback — health checks every 5 seconds, 2 consecutive failures trigger rollback within 60 seconds
- Automatic HTTPS via Let's Encrypt, renewed without intervention
- Built-in error tracking and request analytics — no Sentry subscription needed
Prerequisites
- A Laravel application (8.x or newer)
- Git repository (GitHub, GitLab, or Bitbucket)
composer.jsonat the project root
Project Structure
Temps works with standard Laravel project layouts:
my-laravel-app/
├── app/
├── bootstrap/
├── config/
├── database/
│ └── migrations/
├── public/ # Document root
├── routes/
├── storage/
├── composer.json
└── .env.example
Your .env file stays local — never commit it. You'll set production variables through the Temps CLI.
Deploy Laravel with the Temps CLI
Step 1: Install Temps CLI
# macOS / Linux
curl -fsSL https://temps.sh/install.sh | bash
Step 2: Login and Create Project
bunx @temps-sdk/cli login
# Create project and connect your git repository
bunx @temps-sdk/cli projects create \
-n "My Laravel App" \
-d "Laravel application" \
--repo your-org/your-laravel-app \
--branch main \
--preset php
Temps auto-detects PHP/Laravel projects and configures the Nginx + PHP-FPM stack. Use --preset docker if you already have a Dockerfile.
Step 3: Set Environment Variables
# Application key (generate locally with: php artisan key:generate --show)
bunx @temps-sdk/cli environments vars set APP_KEY "base64:your-key-here" -e production
bunx @temps-sdk/cli environments vars set APP_ENV "production" -e production
bunx @temps-sdk/cli environments vars set APP_DEBUG "false" -e production
# These will be set after provisioning services in Step 4
# bunx @temps-sdk/cli environments vars set DB_CONNECTION "pgsql" -e production
# bunx @temps-sdk/cli environments vars set DATABASE_URL "..." -e production
# bunx @temps-sdk/cli environments vars set REDIS_URL "..." -e production
# Import entire .env.production in one shot
# bunx @temps-sdk/cli environments vars import .env.production -e production
Environment variables are encrypted at rest and injected at runtime — they never appear in image layers.
Step 4: Provision Managed PostgreSQL and Redis
# Create a managed PostgreSQL database
bunx @temps-sdk/cli services create \
--type postgres \
--name laravel-db \
-e production
# Create a managed Redis instance
bunx @temps-sdk/cli services create \
--type redis \
--name laravel-cache \
-e production
Temps provisions the database, generates credentials, and injects DATABASE_URL and REDIS_URL automatically into your environment. Update your config/database.php to read from these:
// config/database.php — PostgreSQL
'pgsql' => [
'driver' => 'pgsql',
'url' => env('DATABASE_URL'),
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '5432'),
'database' => env('DB_DATABASE', 'laravel'),
'username' => env('DB_USERNAME', 'root'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8',
'prefix' => '',
'schema' => 'public',
'sslmode' => 'prefer',
],
Step 5: Deploy
bunx @temps-sdk/cli deploy my-laravel-app -b main -e production -y
Temps will:
- Detect your Laravel application
- Install Composer dependencies (
composer install --no-dev --optimize-autoloader) - Build frontend assets if
package.jsonis present - Run
php artisan migrate --forcebefore traffic switches - Start Nginx + PHP-FPM
- Provision an SSL certificate via Let's Encrypt
Your app is live at your-app.temps.sh within minutes.
Running Artisan Migrations
Migrations run automatically as part of every deploy. Temps executes php artisan migrate --force after the build succeeds and before routing traffic to the new container. If migrations fail, the deploy halts and the previous version keeps serving traffic.
To run Artisan commands manually:
# Open a shell into the running container
bunx @temps-sdk/cli exec -p my-laravel-app -- php artisan tinker
# Run a specific command
bunx @temps-sdk/cli exec -p my-laravel-app -- php artisan migrate:status
# Seed the database
bunx @temps-sdk/cli exec -p my-laravel-app -- php artisan db:seed
Queue Workers
Basic Queue Worker
Add a worker process to your project:
bunx @temps-sdk/cli workers create \
--project my-laravel-app \
--name queue-worker \
--command "php artisan queue:work --sleep=3 --tries=3" \
-e production
The worker starts alongside your app and restarts automatically if it crashes.
Laravel Horizon
Temps supports Laravel Horizon for advanced queue monitoring:
# Install Horizon (in your local project)
composer require laravel/horizon
php artisan horizon:install
# Deploy and start Horizon as a worker
bunx @temps-sdk/cli workers create \
--project my-laravel-app \
--name horizon \
--command "php artisan horizon" \
-e production
Task Scheduler
Run the Laravel scheduler every minute:
bunx @temps-sdk/cli workers create \
--project my-laravel-app \
--name scheduler \
--command "php artisan schedule:run" \
--schedule "* * * * *" \
-e production
Storage and File Uploads
For file uploads in production, use S3-compatible storage (Temps Cloud or your own bucket):
bunx @temps-sdk/cli environments vars set FILESYSTEM_DISK "s3" -e production
bunx @temps-sdk/cli environments vars set AWS_ACCESS_KEY_ID "your-key" -e production
bunx @temps-sdk/cli environments vars set AWS_SECRET_ACCESS_KEY "your-secret" -e production
bunx @temps-sdk/cli environments vars set AWS_DEFAULT_REGION "us-east-1" -e production
bunx @temps-sdk/cli environments vars set AWS_BUCKET "your-bucket" -e production
// Store files
Storage::disk('s3')->put('avatar.jpg', $fileContents);
// Generate URL
$url = Storage::disk('s3')->url('avatar.jpg');
Do not rely on the local filesystem for persistent storage in containerized deployments — containers are ephemeral.
Health Checks
Temps polls your application every 5 seconds. Two consecutive failures within the 60-second error window trigger automatic rollback to the last healthy deployment. Add a health route:
// routes/web.php
Route::get('/health', function () {
return response()->json(['status' => 'ok']);
});
Or in an API context:
// routes/api.php
Route::get('/health', function () {
return response()->json([
'status' => 'ok',
'timestamp' => now()->toISOString(),
]);
});
Keep the health endpoint fast — avoid database queries in the health route when possible.
Environment Variables Reference
# Required
bunx @temps-sdk/cli environments vars set APP_KEY "base64:..." -e production
bunx @temps-sdk/cli environments vars set APP_ENV "production" -e production
bunx @temps-sdk/cli environments vars set APP_DEBUG "false" -e production
bunx @temps-sdk/cli environments vars set APP_URL "https://your-app.temps.sh" -e production
# Database (auto-injected when using managed services, or set manually)
bunx @temps-sdk/cli environments vars set DB_CONNECTION "pgsql" -e production
bunx @temps-sdk/cli environments vars set DATABASE_URL "postgresql://..." -e production
# Cache and sessions
bunx @temps-sdk/cli environments vars set CACHE_DRIVER "redis" -e production
bunx @temps-sdk/cli environments vars set SESSION_DRIVER "redis" -e production
bunx @temps-sdk/cli environments vars set QUEUE_CONNECTION "redis" -e production
bunx @temps-sdk/cli environments vars set REDIS_URL "redis://..." -e production
# Mail
bunx @temps-sdk/cli environments vars set MAIL_MAILER "smtp" -e production
bunx @temps-sdk/cli environments vars set MAIL_HOST "smtp.example.com" -e production
# List all variables (values hidden)
bunx @temps-sdk/cli environments vars list -e production
Built-in Monitoring
After deployment, your Temps dashboard includes monitoring with no additional subscriptions:
Request Analytics
- Request count and latency percentiles (p50, p95, p99)
- Endpoint breakdown with per-route timing
- Error rates by route
- Geographic distribution of traffic
Error Tracking
- Exception capture with full stack traces and request context
- Error grouping and trend analysis
- No Sentry subscription needed — it's in the same binary as your proxy
Stream logs from the CLI:
bunx @temps-sdk/cli runtime-logs -p my-laravel-app -f
Scaling
Horizontal Scaling
Scale to multiple replicas:
bunx @temps-sdk/cli environments scale -e production -r 3
Temps handles load balancing automatically via Pingora, Cloudflare's open-source Rust proxy.
Update Resource Allocation
bunx @temps-sdk/cli projects config -p my-laravel-app \
--cpu-limit 2 \
--memory-limit 1024 \
-y
bunx @temps-sdk/cli deploy my-laravel-app -b main -e production -y
Custom Domains
DNS Configuration
Add an A record pointing to your Temps server IP:
| Type | Name | Value |
|---|---|---|
| A | @ | YOUR_SERVER_IP |
| A | www | YOUR_SERVER_IP |
Adding Your Domain
# HTTP-01 challenge (default)
bunx @temps-sdk/cli domains add -d yourdomain.com
# DNS-01 challenge (for wildcard domains)
bunx @temps-sdk/cli domains add -d "*.yourdomain.com" --challenge dns-01
Let's Encrypt certificates are provisioned and renewed automatically.
Comparing Laravel Deployment Options
Temps vs Laravel Forge vs Ploi vs Shared Hosting
| Feature | Temps | Laravel Forge | Ploi | Shared Hosting |
|---|---|---|---|---|
| Setup time | ~5 minutes | ~10 minutes | ~10 minutes | ~30 minutes |
| Nginx + PHP-FPM | Automatic | Automatic | Automatic | Manual / limited |
| Managed PostgreSQL | Built-in | External provider | External provider | MySQL only (often) |
| Managed Redis | Built-in | Separate provisioning | Separate provisioning | Rarely available |
| Artisan migrations on deploy | Automatic | Manual deploy hooks | Deploy hooks | Manual SSH |
| Queue worker management | Built-in | Daemon config in UI | Daemon config in UI | Not available |
| Laravel Horizon support | Yes | Yes | Yes | No |
| Health checks + auto-rollback | Built-in (5s interval) | Not included | Not included | Not included |
| Built-in error tracking | Yes (no Sentry needed) | No | No | No |
| Built-in request analytics | Yes | No | No | Limited |
| SSL / HTTPS | Automatic (Let's Encrypt) | Automatic | Automatic | Often extra cost |
| Git-push deploy | Yes | Yes | Yes | FTP/SFTP typically |
| Self-hostable | Yes (Apache 2.0, free) | No | No | No |
| Vendor lock-in | None | Forge-managed servers | Ploi-managed servers | Complete |
| Pricing | Free self-host (Temps Cloud add-on coming soon) | See forge.laravel.com/pricing | See ploi.io/pricing | Varies |
The main difference: Temps bundles the things you'd otherwise need Forge + Sentry + a separate analytics tool to cover. If you're running multiple Laravel apps and paying per server on Forge, self-hosting Temps often eliminates those per-server subscriptions entirely.
Troubleshooting
Composer Install Fails
Check build logs:
bunx @temps-sdk/cli deploy my-laravel-app -b main -e production
Common issues:
- Missing PHP extensions (add to your Dockerfile or
nixpacks.toml) - Private Composer packages (set
COMPOSER_AUTHenv var with your token) - Memory limit during install — the build environment allows up to 512MB
App Shows 500 Error
# Stream live logs
bunx @temps-sdk/cli runtime-logs -p my-laravel-app -f
Common causes:
APP_KEYnot set — generate withphp artisan key:generate --showlocally, then set via CLIAPP_DEBUG=truein production (not an error, but reveals internals)- Missing
storage/permissions — ensure your Dockerfile runschmod -R 775 storage bootstrap/cache - Database connection refused — verify
DATABASE_URLis set and the service is running
Migration Fails at Deploy
Migrations run with --force in production. If a migration fails:
- Check
bunx @temps-sdk/cli runtime-logs -p my-laravel-appfor the SQL error - Fix the migration locally and test with a fresh database
- Never edit existing migration files — add a new corrective migration instead
Health Check Failures
// routes/web.php — keep this simple and fast
Route::get('/health', fn() => response()->json(['status' => 'ok']));
If your app takes more than 2 seconds to respond under normal load, the health check may time out. Keep the health endpoint free from database and cache queries.
FAQ
Do I need Laravel Forge?
No. Forge is a server management tool that provisions and configures Linux servers for you. Temps does the same — auto-configures Nginx, PHP-FPM, queues, and SSL — but it also includes built-in error tracking, request analytics, and health-checked deployments that Forge doesn't provide. And Temps is self-hostable for free, whereas Forge is a subscription service (see forge.laravel.com/pricing for current pricing).
How do I run Artisan migrate?
Temps runs php artisan migrate --force automatically on every deploy before switching traffic to the new container. To run migrations or any Artisan command manually, use:
bunx @temps-sdk/cli exec -p my-laravel-app -- php artisan migrate --force
Does Temps support Laravel queues?
Yes. You can run a standard queue worker, Laravel Horizon, or the task scheduler as persistent worker processes. Add them with bunx @temps-sdk/cli workers create. Workers restart automatically if they crash, and you can view their logs in the Temps dashboard.
Is Temps free?
Yes — Temps is Apache 2.0 and completely free to self-host on any Linux server. Temps Cloud is a separate, not-yet-shipped managed add-on for telemetry retention, offsite backups, and AI credits (pricing not yet announced). You own your data and can migrate away at any time.
What PHP version does Temps use?
Temps uses Nixpacks to auto-detect your PHP version from composer.json. You can pin a specific PHP version by adding a nixpacks.toml to your project root:
# nixpacks.toml
[phases.setup]
nixPkgs = ["php83", "php83Extensions.pdo", "php83Extensions.pgsql", "php83Extensions.redis"]
Does Temps support Laravel Octane?
Yes — deploy Octane with Swoole or RoadRunner by setting the appropriate OCTANE_SERVER environment variable and adjusting your start command in a Dockerfile:
CMD ["php", "artisan", "octane:start", "--server=swoole", "--host=0.0.0.0", "--port=8080"]
Quick Reference
# Install CLI
curl -fsSL https://temps.sh/install.sh | bash
# Login
bunx @temps-sdk/cli login
# Create project and connect repo
bunx @temps-sdk/cli projects create \
-n "My Laravel App" \
-d "Laravel application" \
--repo myorg/my-laravel-app \
--branch main \
--preset php
# Set required env vars
bunx @temps-sdk/cli environments vars set APP_KEY "base64:..." -e production
bunx @temps-sdk/cli environments vars set APP_ENV "production" -e production
bunx @temps-sdk/cli environments vars set APP_DEBUG "false" -e production
# Provision services
bunx @temps-sdk/cli services create --type postgres --name laravel-db -e production
bunx @temps-sdk/cli services create --type redis --name laravel-cache -e production
# Deploy
bunx @temps-sdk/cli deploy my-laravel-app -b main -e production -y
# Stream logs
bunx @temps-sdk/cli runtime-logs -p my-laravel-app -f
# Scale replicas
bunx @temps-sdk/cli environments scale -e production -r 3
# Run Artisan command
bunx @temps-sdk/cli exec -p my-laravel-app -- php artisan migrate:status
# Add domain
bunx @temps-sdk/cli domains add -d yourdomain.com
Ready to deploy your Laravel app? Get started at temps.sh — self-host for free (Apache 2.0), with a managed Temps Cloud add-on coming soon:
curl -fsSL https://temps.sh/install.sh | bash && bunx @temps-sdk/cli login
Get weekly updates