Deploying a Laravel application, especially one with a powerful admin panel like Filament v3, often relies on command-line tools like Composer, NPM, and Artisan. But what if your production environment—like many shared hosting providers—restricts terminal access?
This comprehensive guide walks you through a precise, step-by-step workflow to build and deploy a production-ready Laravel 13 application with Filament v3, specifically tailored for environments where you cannot run terminal commands or build tools on the target server. We'll prepare everything locally, package it, and deploy it with minimal server interaction.
Phase 1: Local Environment & Core Installation
We begin by setting up your project locally, ensuring all dependencies are handled before deployment.
Initialize the Laravel Project
Start with a fresh Laravel instance on your local machine. This example uses a "restaurant-app" name.
composer create-project laravel/laravel restaurant-app
cd restaurant-app
Configure Local Environment
Update your .env file with your local database details. Remember, this file will be recreated on the server.
APP_NAME="Flavor Harbor"
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL=http://127.0.0.1:8000
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=restaurant_db
DB_USERNAME=root
DB_PASSWORD=
Generate your application's security key:
php artisan key:generate
Install Filament v3 Admin Panel
Install Filament via Composer and run its panel installer. The -W flag resolves any dependency conflicts.
composer require filament/filament:"^3.2" -W
php artisan filament:install --panels
Create the Initial Admin User
Generate your first admin account to access the Filament panel.
php artisan make:filament-user
Phase 2: Database Architecture & Models
Next, we define our application's data structure and relationships.
Create Database Migrations
Generate models and their corresponding migrations for your core content management:
php artisan make:model Category -m
php artisan make:model MenuItem -m
php artisan make:model Page -m
php artisan make:model Setting -m
Define Table Schemas (database/migrations/)
Edit the generated migration files to define your table structures.
// database/migrations/YYYY_MM_DD_create_categories_table.php
use IlluminateDatabaseMigrationsMigration;
use IlluminateDatabaseSchemaBlueprint;
use IlluminateSupportFacadesSchema;
return new class extends Migration
{
public function up(): void
{
Schema::create('categories', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('slug')->unique();
$table->unsignedInteger('sort_order')->default(0);
$table->boolean('is_active')->default(true);
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('categories');
}
};
// database/migrations/YYYY_MM_DD_create_menu_items_table.php
use IlluminateDatabaseMigrationsMigration;
use IlluminateDatabaseSchemaBlueprint;
use IlluminateSupportFacadesSchema;
return new class extends Migration
{
public function up(): void
{
Schema::create('menu_items', function (Blueprint $table) {
$table->id();
$table->foreignId('category_id')->constrained()->onDelete('cascade');
$table->string('name');
$table->text('description')->nullable();
$table->decimal('price', 8, 2);
$table->string('image')->nullable();
$table->boolean('is_available')->default(true);
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('menu_items');
}
};
Similarly, define schemas for pages (title, slug, content, meta_description) and settings (key, value).
Establish Eloquent Relationships (app/Models/)
Define the relationships between your models for easy data access.
// app/Models/Category.php
namespace AppModels;
use IlluminateDatabaseEloquentFactoriesHasFactory;
use IlluminateDatabaseEloquentModel;
use IlluminateDatabaseEloquentRelationsHasMany;
class Category extends Model
{
use HasFactory;
protected $fillable = ['name', 'slug', 'sort_order', 'is_active'];
public function menuItems(): HasMany
{
return $this->hasMany(MenuItem::class);
}
}
// app/Models/MenuItem.php
namespace AppModels;
use IlluminateDatabaseEloquentFactoriesHasFactory;
use IlluminateDatabaseEloquentModel;
use IlluminateDatabaseEloquentRelationsBelongsTo;
class MenuItem extends Model
{
use HasFactory;
protected $fillable = ['category_id', 'name', 'description', 'price', 'image', 'is_available'];
public function category(): BelongsTo
{
return $this->belongsTo(Category::class);
}
}
Run Initial Migrations
Apply your database schema changes.
php artisan migrate
Phase 3: Filament Admin CMS Configuration
Configure the Filament admin panel to manage your dynamic content.
Generate Filament Resources
Create the CRUD interfaces for each of your models:
php artisan make:filament-resource Category
php artisan make:filament-resource MenuItem
php artisan make:filament-resource Page
php artisan make:filament-resource Setting
Configure Form Schemas & Tables (app/Filament/Resources/)
Edit the generated Filament resources to define your forms and table columns.
- File Uploads: Use
FileUpload::make('image')->directory('menu-items')for automatic public disk handling. - Relationships: Use
Select::make('category_id')->relationship('category', 'name'). - Toggles & Selectors: Implement status switches (
Toggle::make('is_active')) and rich text editors (RichEditor::make('content')).
Publish Filament Assets
This is a critical step for terminal-free deployment. Publish Filament's pre-compiled CSS/JS assets directly into your public/ directory. This ensures the admin panel runs without needing runtime asset generation (e.g., Vite/NPM) on the server.
php artisan filament:assets
Phase 4: Frontend Routing & Public Asset Handling
We'll define how your public website works and ensure static assets are correctly served.
Structure Public Assets (public/)
To avoid runtime Node.js/NPM/Vite compilation, serve plain assets directly from public/.
-
public/css/custom.css(for custom styles, fonts, and brand variables) -
public/js/custom.js(for DOM interactions, scroll observers, etc.)
Define Application Routes (routes/web.php)
Map your frontend views and dynamic CMS pages.
// routes/web.php
use AppModelsCategory;
use AppModelsPage;
use AppModelsSetting;
use IlluminateSupportFacadesArtisan;
use IlluminateSupportFacadesRoute;
// Main Home / Menu View
Route::get('/', function () {
$categories = Category::with(['menuItems' => fn($q) => $q->where('is_available', true)])
->where('is_active', true)
->get();
$settings = Setting::pluck('value', 'key')->toArray();
return view('index', compact('categories', 'settings'));
});
// Dynamic CMS Page Slug Handler
Route::get('/{slug}', function ($slug) {
$page = Page::where('slug', $slug)->firstOrFail();
$settings = Setting::pluck('value', 'key')->toArray();
return view('page', compact('page', 'settings'));
});
Terminal-Free Automated Setup Route
This route is the cornerstone of your terminal-free deployment. It allows you to run essential Artisan commands via a web browser after uploading. Remember to remove or secure this route after initial setup on a production server!
// routes/web.php (add this route)
Route::get('/system-setup-run', function () {
Artisan::call('migrate --force');
Artisan::call('config:clear');
Artisan::call('cache:clear');
if (!file_exists(public_path('storage'))) {
// Ensure the storage symlink exists
app('files')->link(storage_path('app/public'), public_path('storage'));
}
return 'System setup executed successfully!';
});
Phase 5: Production Packaging & Terminal-Free Deployment
This phase details how to prepare your application for upload and the steps on the shared host.
Before You Upload: Local Preparation
Before creating your deployment package, ensure your local environment meets production requirements and all necessary files are included.
-
Hosting Requirements Check:
- PHP 8.3+ (as specified in
composer.json) - Essential PHP Extensions:
openssl,pdo,mbstring,tokenizer,xml,ctype,json,bcmath,fileinfo. - MySQL database.
- Verify these in your cPanel's "Select PHP Version" tool.
- PHP 8.3+ (as specified in
-
Install Production PHP Dependencies Locally:
Run Composer to install all production-only dependencies and optimize the autoloader. This is crucial becausevendor/must be included in your zip.
composer install --optimize-autoloader --no-dev -
Build Frontend Assets (if needed):
If your frontend uses Vite or other build tools (even though we're trying to avoid runtime compilation, if you have any custom Vite assets for the public site, build them locally):
npm install npm run build -
Clear Local Caches & Log Files:
Remove all temporary framework files and logs to keep your package clean and prevent stale data.
php artisan config:clear php artisan cache:clear rm -rf storage/logs/* storage/framework/cache/data/* storage/framework/sessions/* storage/framework/views/* -
Export MySQL Database Dump (Optional):
If you have local data (e.g., categories, menu items) you want to transfer, dump your local database.
mysqldump -u root -p restaurant_db > database.sql
Generate Deployment Package (ZIP)
Create a comprehensive ZIP archive. This archive MUST include the vendor/ directory (since the server won't run Composer) but exclude development-only files.
zip -r application-package.zip .
-x "node_modules/*"
-x ".git/*"
-x ".env"
-x "database/*.sqlite*"
-x "storage/logs/*"
-x "tests/*"
-x "storage/framework/cache/*"
-x "storage/framework/sessions/*"
-x "storage/framework/views/*"
Important: Do not include your local
.envfile in the package. You'll create a new one on the server.
Deploy to Target Server (cPanel / Shared Host)
-
Upload and Extract:
- Log in to cPanel -> File Manager.
- Navigate to your domain's root folder (e.g.,
public_htmlor a subdomain folder). - Upload
application-package.zipand extract it. This should create a folder (e.g.,restaurant-app) containing your Laravel project.
-
Create MySQL Database:
- In cPanel -> MySQL Databases.
- Create a new database (e.g.,
username_restaurant). - Create a new database user with a strong password.
- Add the user to the database with ALL PRIVILEGES. Note down the database name, username, and password.
-
Configure
.envon the Server:- In File Manager, navigate to your extracted project folder (
public_html/restaurant-app/). - Copy
.env.exampleto.env. - Edit
.envwith your production details and the MySQL credentials you just created.
APP_NAME="Restaurant App" APP_ENV=production APP_KEY= # Generate this using your browser setup route, or manually if you have terminal access APP_DEBUG=false APP_URL=https://yourdomain.com DB_CONNECTION=mysql DB_HOST=localhost DB_DATABASE=username_restaurant DB_USERNAME=username_dbuser DB_PASSWORD=your_db_password SESSION_DRIVER=database CACHE_STORE=database QUEUE_CONNECTION=database - In File Manager, navigate to your extracted project folder (
-
Point the Domain to Laravel’s
publicFolder:
Laravel must serve from itspublic/directory, not the project root.- Best Method (cPanel): cPanel -> Domains -> your domain -> Document Root. Set it to
/home/username/public_html/restaurant-app/public. - Alternative (.htaccess redirect): If you can't change the document root, create or edit
public_html/.htaccess(at the same level as yourrestaurant-appfolder) to redirect:
# public_html/.htaccess <IfModule mod_rewrite.c> RewriteEngine On RewriteRule ^(.*)$ restaurant-app/public/$1 [L] </IfModule> - Best Method (cPanel): cPanel -> Domains -> your domain -> Document Root. Set it to
-
Run Terminal-Free Setup:
Navigate tohttps://yourdomain.com/system-setup-runin your web browser. This will:- Run
php artisan migrate --force - Clear config and cache
- Create the
public/storagesymlink (essential for uploaded images). - This also generates your
APP_KEYif it was empty in.env. > Security Warning: After successfully running this once, remove or protect this route (/system-setup-run) inroutes/web.phpto prevent unauthorized access to system commands.
- Run
-
Fix Folder Permissions:
Laravel needs write access tostorageandbootstrap/cache.- In cPanel -> File Manager: right-click on
storageandbootstrap/cachefolders, then select "Change Permissions" and set them to775. Apply recursively forstorage.
- In cPanel -> File Manager: right-click on
Import Your Data (Optional):
If you dumped your database earlier, importdatabase.sqlinto your new MySQL database via cPanel -> phpMyAdmin.-
Create Admin User:
If you didn't import an admin user, you'll need one. Since you can't runphp artisan make:filament-userdirectly, you can:- Temporarily add a user creation script to a route (and then remove it).
- Manually insert a user into the
userstable via phpMyAdmin, then use Filament's password reset feature.
Your Filament admin panel will be accessible at https://yourdomain.com/admin.
Key Takeaways
- Local Preparation is Key: All Composer and NPM commands (including
composer install --no-devandnpm run build) must be executed locally. - Include
vendor/: Your deployment ZIP must contain thevendor/directory, optimized for production. - Publish Filament Assets: Run
php artisan filament:assetslocally to pre-compile Filament's CSS/JS. - Automated Setup Route: The
/system-setup-runroute is your lifeline for migrations and storage linking without SSH. Secure it immediately after use! -
public/as Document Root: Ensure your web server points directly to thepublic/folder of your Laravel application. - Permissions: Correct folder permissions (
775forstorageandbootstrap/cache) are vital for write access.
GitHub Repository
Explore the full source code for the "Flavor Harbor" restaurant website, including the Filament v3 admin panel, on GitHub:
d5b94396feba3
/
fullstack-rastaurant-website-filament-laravel
A full-stack restaurant website for FLAVOR HARBOR: a public-facing dining site with menu browsing, cart checkout, and table reservations, plus a Filament admin panel for kitchen menu and CMS content.
Flavor Harbor — Restaurant Website (Laravel + Filament)
A full-stack restaurant website for FLAVOR HARBOR: a public-facing dining site with menu browsing, cart checkout, and table reservations, plus a Filament admin panel for kitchen menu and CMS content.
Stack
Layer
Technology
Backend
Laravel 13 (PHP 8.3+)
Admin
Filament 3 panel at /admin
Frontend
Blade, Alpine.js, Vite 8, Tailwind CSS 4
Database
MySQL (configurable via
.env)
Laravel
Laravel powers routing, Eloquent models, migrations, authentication for the admin panel, file storage for menu images, and the public site views. Core domain models:
- Category — menu sections (active/inactive)
- MenuItem — dishes with price, image, description, availability
- Page — CMS pages with slug, rich content, and SEO fields
- Setting — key/value site configuration (branding, hero, contact, social links)
Filament
Filament provides the Kitchen Ops admin UI (FLAVOR HARBOR | Kitchen Ops) at /admin with:
-
Kitchen Menu
- Categories (name, slug, active…
Have you deployed Laravel to shared hosting without SSH? Share your tips, tricks, or challenges in the comments below! If you found this guide helpful, consider following me for more in-depth Laravel and Dev.to content.
Top comments (0)