We earn commissions when you shop through the links below.
If you want to deploy Laravel app on Railway step by step without wrestling with Nginx configs, server provisioning, or SSH tunnels, you’re in the right place. Railway is one of the cleanest platforms I’ve used for shipping Laravel apps fast — you connect a repo, add a database, set env vars, and you’re live. This guide walks through the entire process from a fresh Laravel project to a production URL.
Why Railway for Laravel?
Railway handles the infrastructure layer so you can focus on your app. It supports PHP natively via Nixpacks, provisions MySQL or PostgreSQL databases in seconds, and gives you environment variable management, deployment logs, and rollbacks all in one dashboard. The free tier is generous enough to prototype, and scaling is a slider away.
If you need a more traditional shared hosting setup, Hostinger is worth a look for budget-conscious projects. But for Git-driven, container-based deployments, Railway wins.
Prerequisites
- A Laravel project pushed to a GitHub repository
- A Railway account (free tier works)
- Basic familiarity with
.envfiles and Artisan commands
Step 1 — Prepare Your Laravel Project
Before touching Railway, make sure your app is production-ready locally. Run:
composer install --optimize-autoloader --no-dev
php artisan config:cache
php artisan route:cache
php artisan view:cache
You don’t need to commit the cache files — Railway runs these during the build. What you do need is a Procfile in your project root to tell Railway how to start the web server:
web: vendor/bin/heroku-php-apache2 public/
Railway’s Nixpacks builder supports this Procfile convention out of the box. If you prefer Nginx, use:
web: vendor/bin/heroku-php-nginx public/
Commit the Procfile and push to GitHub before moving on.
Step 2 — Create a Railway Project
- Log in to Railway and click New Project.
- Select Deploy from GitHub repo.
- Authorize Railway to access your repositories if this is your first time.
- Pick the repo containing your Laravel app.
Railway will detect PHP automatically via Nixpacks and kick off an initial build. It will likely fail at this point because we haven’t set environment variables yet — that’s expected.
Step 3 — Add a MySQL Database
Inside your Railway project dashboard, click New Service → Database → MySQL. Railway spins up a managed MySQL instance in about 30 seconds and injects connection variables into your project’s environment automatically.
The variables you’ll get are:
MYSQLHOST
MYSQLPORT
MYSQLDATABASE
MYSQLUSER
MYSQLPASSWORD
Laravel’s config/database.php expects different keys (DB_HOST, DB_PORT, etc.), so you’ll map them in the next step.
Step 4 — Configure Environment Variables
In your Laravel service (not the database service), go to Variables and add the following. Use the Railway MySQL values from the database service panel:
APP_NAME=YourAppName
APP_ENV=production
APP_KEY=base64:GENERATE_THIS_LOCALLY
APP_DEBUG=false
APP_URL=https://your-railway-domain.up.railway.app
DB_CONNECTION=mysql
DB_HOST=${{MySQL.MYSQLHOST}}
DB_PORT=${{MySQL.MYSQLPORT}}
DB_DATABASE=${{MySQL.MYSQLDATABASE}}
DB_USERNAME=${{MySQL.MYSQLUSER}}
DB_PASSWORD=${{MySQL.MYSQLPASSWORD}}
CACHE_DRIVER=database
SESSION_DRIVER=database
QUEUE_CONNECTION=database
LOG_CHANNEL=stderr
The ${{MySQL.MYSQLHOST}} syntax is Railway’s reference variables feature — it pulls the value dynamically from the sibling MySQL service. You don’t hardcode credentials.
Generate your APP_KEY locally with php artisan key:generate --show and paste the result into Railway.
Setting LOG_CHANNEL=stderr is important — it sends Laravel logs to Railway’s log stream so you can read them in the dashboard.
Step 5 — Add a Build Command for Migrations
You want migrations to run automatically on every deploy. Railway lets you set a custom start command. Instead of editing the Procfile for this, go to your service settings and set the Start Command to:
php artisan migrate --force && vendor/bin/heroku-php-apache2 public/
The --force flag skips the production confirmation prompt. This ensures your schema is always in sync when Railway deploys a new version.
If you also need to seed data on first deploy, use:
php artisan migrate --force && php artisan db:seed --force && vendor/bin/heroku-php-apache2 public/
Step 6 — Trigger a Deployment
With env vars and the start command configured, trigger a manual redeploy from the Railway dashboard or push a new commit to your GitHub repo. Railway will:
- Pull your code
- Run Nixpacks to build the PHP environment and install Composer dependencies
- Start the web process using your start command
- Run migrations against the Railway MySQL instance
- Expose the app on a
*.up.railway.appsubdomain
Watch the build logs in real time from the deployment panel. If something breaks, the error output is right there — no SSH required.
Step 7 — Add a Custom Domain (Optional)
Railway makes custom domains straightforward. In your service settings, go to Networking → Custom Domain, enter your domain, and Railway gives you a CNAME record to add to your DNS provider. TLS is provisioned automatically via Let’s Encrypt.
Once DNS propagates, update your APP_URL variable to match the custom domain.
Handling Storage and File Uploads
Railway’s filesystem is ephemeral — files written to storage/app won’t persist across deploys. For file uploads you have two options:
- Use S3-compatible storage (AWS S3, Cloudflare R2, Backblaze B2). Set
FILESYSTEM_DISK=s3and add the relevant S3 env vars. - Use Railway Volumes — Railway supports persistent volumes you can mount at a path like
/app/storage.
For most production apps, S3-compatible storage is the better long-term choice. Run php artisan storage:link as part of your start command if you’re serving local files:
php artisan storage:link && php artisan migrate --force && vendor/bin/heroku-php-apache2 public/
Queue Workers on Railway
If your Laravel app uses queues, add a second service in the same Railway project. Point it to the same GitHub repo but set the start command to:
php artisan queue:work --sleep=3 --tries=3 --max-time=3600
Give it the same environment variables as your web service. Railway will run both processes independently, and you can scale them separately.
Common Issues
500 errors after deploy: Usually an APP_KEY mismatch or missing env var. Check the logs via the Railway dashboard — with LOG_CHANNEL=stderr set, all Laravel exceptions stream directly there.
Migrations failing: Verify the DB connection variables are mapped correctly. Use Railway’s Connect tab on the MySQL service to get a one-time connection string for testing locally.
Static assets missing: Run npm run build locally, commit the public/build directory, and push. Railway doesn’t run Node builds by default unless you configure it, so committing compiled assets is the easiest path.
Final Thoughts
To deploy Laravel app on Railway step by step, the core flow is: Procfile → GitHub repo → Railway project → MySQL service → env vars → start command with migrations → deploy. The whole process takes under 15 minutes once you’ve done it once, and subsequent deploys are just git pushes.
Railway is genuinely one of the best platforms for Laravel side projects and small-to-medium production apps. The developer experience is miles ahead of manually managing a VPS, and the pricing scales predictably. If you find yourself needing managed WordPress or simpler PHP hosting for a client project alongside your Laravel work, Hostinger fills that gap without breaking the budget.
Now go ship something.