DEV Community

Cover image for Laravel 13 & Filament v3: Production-Ready Deployment on Shared Hosting (Terminal-Free Guide)
Shahibur Rahman
Shahibur Rahman

Posted on

Laravel 13 & Filament v3: Production-Ready Deployment on Shared Hosting (Terminal-Free Guide)

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
Enter fullscreen mode Exit fullscreen mode

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=
Enter fullscreen mode Exit fullscreen mode

Generate your application's security key:

php artisan key:generate
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Create the Initial Admin User

Generate your first admin account to access the Filament panel.

php artisan make:filament-user
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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');
    }
};
Enter fullscreen mode Exit fullscreen mode
// 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');
    }
};
Enter fullscreen mode Exit fullscreen mode

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);
    }
}
Enter fullscreen mode Exit fullscreen mode
// 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);
    }
}
Enter fullscreen mode Exit fullscreen mode

Run Initial Migrations

Apply your database schema changes.

php artisan migrate
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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'));
});
Enter fullscreen mode Exit fullscreen mode

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!';
});
Enter fullscreen mode Exit fullscreen mode

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.

  1. 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.
  2. Install Production PHP Dependencies Locally:
    Run Composer to install all production-only dependencies and optimize the autoloader. This is crucial because vendor/ must be included in your zip.

    composer install --optimize-autoloader --no-dev
    
  3. 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
    
  4. 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/*
    
  5. 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/*"
Enter fullscreen mode Exit fullscreen mode

Important: Do not include your local .env file in the package. You'll create a new one on the server.

Deploy to Target Server (cPanel / Shared Host)

  1. Upload and Extract:

    • Log in to cPanel -> File Manager.
    • Navigate to your domain's root folder (e.g., public_html or a subdomain folder).
    • Upload application-package.zip and extract it. This should create a folder (e.g., restaurant-app) containing your Laravel project.
  2. 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.
  3. Configure .env on the Server:

    • In File Manager, navigate to your extracted project folder (public_html/restaurant-app/).
    • Copy .env.example to .env.
    • Edit .env with 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
    
  4. Point the Domain to Laravel’s public Folder:
    Laravel must serve from its public/ 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 your restaurant-app folder) to redirect:
    # public_html/.htaccess
    <IfModule mod_rewrite.c>
        RewriteEngine On
        RewriteRule ^(.*)$ restaurant-app/public/$1 [L]
    </IfModule>
    
  5. Run Terminal-Free Setup:
    Navigate to https://yourdomain.com/system-setup-run in your web browser. This will:

    • Run php artisan migrate --force
    • Clear config and cache
    • Create the public/storage symlink (essential for uploaded images).
    • This also generates your APP_KEY if it was empty in .env. > Security Warning: After successfully running this once, remove or protect this route (/system-setup-run) in routes/web.php to prevent unauthorized access to system commands.
  6. Fix Folder Permissions:
    Laravel needs write access to storage and bootstrap/cache.

    • In cPanel -> File Manager: right-click on storage and bootstrap/cache folders, then select "Change Permissions" and set them to 775. Apply recursively for storage.
  7. Import Your Data (Optional):
    If you dumped your database earlier, import database.sql into your new MySQL database via cPanel -> phpMyAdmin.

  8. Create Admin User:
    If you didn't import an admin user, you'll need one. Since you can't run php artisan make:filament-user directly, you can:

    • Temporarily add a user creation script to a route (and then remove it).
    • Manually insert a user into the users table 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-dev and npm run build) must be executed locally.
  • Include vendor/: Your deployment ZIP must contain the vendor/ directory, optimized for production.
  • Publish Filament Assets: Run php artisan filament:assets locally to pre-compile Filament's CSS/JS.
  • Automated Setup Route: The /system-setup-run route 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 the public/ folder of your Laravel application.
  • Permissions: Correct folder permissions (775 for storage and bootstrap/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:

GitHub logo 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)