Meta POS User Guide Docs

Installation & setup

Meta POS is a Laravel 13 application (PHP 8.3+). This page covers local development (XAMPP / php artisan serve), cloud & shared hosting, how the web server must point at public/index.php, how to configure .env, and exactly what the built-in install wizard does.

You only do this once

Installation is a one-time setup. If the POS is already installed, skip to Getting started. License / purchase codes are managed later under Settings → Updates — not inside the install wizard.

Before you start

PHP 8.3+ host

Local stack (XAMPP / Laragon / Herd) or cloud hosting with PHP 8.3 or newer — required by Laravel 13.

A database

An empty MySQL / MariaDB database (typical for production), or SQLite for quick local trials.

App package

The Meta POS project files. For source installs you also need Composer and Node.js to build assets.

Server requirements

The wizard’s first screen checks PHP version, required extensions, and writable folders. Summary:

RequirementNeededNotes
PHP8.3 or newerLaravel 13 / Meta POS require PHP 8.3+. Set this in cPanel, Plesk, or your local stack.
DatabaseMySQL 8+ / MariaDB 10.3+ or SQLiteProduction: MySQL. Local quickstart: SQLite file under database/.
PHP extensionsPDO, Mbstring, OpenSSL, Tokenizer, XML, Ctype, JSON, FileinfoAlso enable pdo_mysql (or pdo_sqlite), curl, gd, zip, and intl when available.
Writable foldersYesstorage/ (including app, framework, logs) and bootstrap/cache/.
Web rootpublic/Only the public folder should be web-accessible. See Document root & index.php.
Composer / NodeLocal & VPS buildsNot needed on shared hosts if you upload a pre-built package with vendor/ and compiled public/build assets.

Choose an install path

Local installation

Use this when developing on your PC or testing before going live.

1. Install tools

  • PHP 8.3+ with the extensions above (XAMPP Lite / full XAMPP, Laragon, Laravel Herd, or a system PHP).
  • Composer (PHP dependency manager).
  • Node.js 20+ and npm (to compile Vite front-end assets).
  • MySQL (or use SQLite and skip creating a database).

2. Get the project & install dependencies

From the project root (the folder that contains artisan, composer.json, and public/):

composer install
copy .env.example .env          # Windows
# cp .env.example .env          # macOS / Linux
php artisan key:generate
npm install
npm run build

Or run the bundled Composer setup script (installs deps, copies .env, generates the key, migrates, and builds assets):

composer run setup

composer run setup runs migrations immediately. If you prefer the web wizard instead, skip that script — use composer install, copy .env, generate the key, build assets, leave APP_INSTALLED=false, then open the site and complete the wizard.

3. Prepare the database

MySQL (recommended, matches production): create an empty database (e.g. meta_pos) in phpMyAdmin or the MySQL CLI, then set these in .env:

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=meta_pos
DB_USERNAME=root
DB_PASSWORD=

SQLite (fastest local trial):

DB_CONNECTION=sqlite
DB_DATABASE=database/database.sqlite

Create the empty file if needed: database/database.sqlite (or let the wizard create it when you choose SQLite).

4. Serve the app

Option A — Laravel’s built-in server (simplest):

php artisan serve

Open http://127.0.0.1:8000. Set APP_URL=http://127.0.0.1:8000 in .env.

Option B — XAMPP / Apache virtual host (production-like):

  1. Point DocumentRoot at public/

    Example: if the project lives at C:\xampp\www\meta-pos, the vhost DocumentRoot must be C:\xampp\www\meta-pos\publicnot the project root. See Document root & index.php.

  2. Set APP_URL to the exact base URL

    e.g. http://meta-pos.test or http://localhost/meta-pos/public if you access the app via a subdirectory URL.

  3. Open the site

    If APP_INSTALLED is false and no admin user exists, you are redirected to /install.

For day-to-day coding, composer run dev starts the HTTP server, queue listener, logs, and Vite together. For a one-time install test, php artisan serve plus a built asset bundle is enough.

Document root & public/index.php

Laravel’s front controller is public/index.php. That file boots Composer’s autoloader, loads bootstrap/app.php, and handles every HTTP request. The matching public/.htaccess rewrites pretty URLs to index.php.

Correct layout:

meta-pos/                 ← project root (not web-public)
├── app/
├── bootstrap/
├── config/
├── database/
├── public/               ← ONLY this folder is the website root
│   ├── index.php         ← front controller
│   ├── .htaccess
│   ├── build/            ← Vite compiled assets
│   └── documentation/
├── resources/
├── routes/
├── storage/
├── vendor/
├── .env
└── artisan

Preferred: point the domain at public/

  • Apache / cPanel: set Document Root to .../meta-pos/public (or use “Domains → Document Root”).
  • Nginx: set root to the public path and pass PHP to index.php.
  • VPS / Forge / Ploi: web directory = public.

Never expose .env, vendor/, or storage/logs as public URLs.

Fallback: host forces DocumentRoot = project root

If you cannot change DocumentRoot (some shared hosts only allow public_html), either:

  1. Deploy so public/ contents live in public_html

    Move/copy the contents of public/ into public_html, keep the rest of Laravel one level above (or beside) it, and edit index.php paths so __DIR__.'/../…' still points at vendor and bootstrap.

  2. Or add a root redirect into public/

    Place this index.php in the folder your host treats as the site root (only if DocumentRoot cannot be changed):

    <?php
    /**
     * Temporary bridge when DocumentRoot cannot be set to /public.
     * Prefer fixing DocumentRoot instead of using this long-term.
     */
    require __DIR__.'/public/index.php';

    Also ensure requests for assets under /build, /documentation, etc. resolve under public/ (rewrite rules or an .htaccess that routes into public). Fixing DocumentRoot is safer and simpler.

Do not move only index.php without adjusting the ../vendor and ../bootstrap paths. A blank page or “autoload failed” almost always means the web root is wrong or those relative paths are broken.

Working with .env

The .env file at the project root is the live configuration. Start from .env.example. Never commit real secrets to git.

Core application keys

KeyPurpose
APP_NAMEDisplay name (default Meta POS).
APP_ENVlocal while developing; production on live servers.
APP_KEYEncryption key. Generate with php artisan key:generate. Required before the app can run safely.
APP_DEBUGtrue locally; must be false in production.
APP_URLExact public base URL (scheme + host + path). Used for links, assets, and redirects.
APP_INSTALLEDfalse until setup finishes. The wizard sets this to true when install completes.
APP_LOCALEDefault UI language (e.g. en).

Database keys

Written by the wizard’s database step (or set manually before install):

DB_CONNECTION=mysql   # or sqlite
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=meta_pos
DB_USERNAME=root
DB_PASSWORD=secret

For SQLite, DB_DATABASE is a file path (relative paths like database/database.sqlite are resolved from the project).

Sessions, files, queue, mail

KeyTypical valueNotes
SESSION_DRIVERfileWorks on shared hosting without Redis.
FILESYSTEM_DISKlocalLocal disk for uploads.
STORAGE_PUBLIC_MODElink or directorylink = public/storage symlink. If the host blocks symlinks, the installer falls back to directory (files under public/storage).
QUEUE_CONNECTIONsyncFine for most installs. Use database/redis + a worker on larger sites.
CACHE_STOREfileNo Redis required.
MAIL_*SMTP settingsNeeded for scheduled reports and password emails. Locally, MAIL_MAILER=log writes to the log.

Practical .env tips

  • After changing .env on a cached production server, run php artisan config:clear (or rebuild config cache).
  • Values with spaces or # must be quoted: APP_NAME="Meta POS".
  • Payment gateway, SMS, and FCM keys in .env.example are optional — configure them when you enable those features.
  • The installer updates DB_* and APP_INSTALLED via its EnvWriter; you can still edit .env by hand afterward.

Cloud & shared hosting

  1. Create an empty MySQL database

    In cPanel / Plesk / your provider panel, create a database and user, grant full rights, and note host, name, username, and password.

  2. Upload the application

    Upload the full project (including vendor/ and built front-end assets if you are not running Composer/npm on the server). Keep the Laravel folder structure intact.

  3. Point the domain at public/

    Set Document Root to the public directory. Confirm public/index.php and public/.htaccess are present. See Document root & index.php.

  4. Create .env and set permissions

    Copy .env.example.env on the server (or upload a prepared file). Set APP_URL to your HTTPS domain, APP_ENV=production, APP_DEBUG=false, APP_INSTALLED=false. Generate APP_KEY with php artisan key:generate over SSH, or ship a key already generated on your machine. Make storage/ and bootstrap/cache/ writable by the web user (typically 775 / owner web user).

  5. Open the site → run the wizard

    Visit your domain. You should land on /install. Complete the steps in Installation wizard.

  6. Add the scheduler cron (recommended)

    Scheduled reports, automated backups, stock archives, and update checks need Laravel’s scheduler. Add one cron entry:

    * * * * * cd /path/to/meta-pos && php artisan schedule:run >> /dev/null 2>&1

    Replace /path/to/meta-pos with the real project root (the folder that contains artisan).

  7. Use HTTPS

    Enable SSL (Let’s Encrypt or your host’s certificate). Keep APP_URL on https://….

Shared hosting friendly

Default drivers use the filesystem (file sessions/cache, sync queue). No Redis, Supervisor, or Node process is required on the live server once assets are built. Symlink creation is attempted for public/storage; if blocked, Meta POS falls back to a normal public/storage directory.

Installation wizard

When the app is not installed, middleware redirects visitors to /install. The wizard has five steps:

The installer wizard showing the requirements check step
Step 1 — server requirements with pass/fail messages.
  1. Requirements

    Checks PHP 8.3+, required extensions, and writable storage / bootstrap/cache paths. Fix any failures, refresh, then continue.

  2. Database

    Choose MySQL / MariaDB or SQLite. For MySQL enter host, port, database name, username, and password. The wizard tests the connection, writes the DB settings into .env, and leaves APP_INSTALLED=false until the end.

  3. Admin & first store

    Enter the first store name, admin name, email, and password (min. 8 characters, confirmed). These credentials are your initial Admin login.

  4. Industry & demo data

    Pick Retail, Pharmacy, or Supermarket. Optionally load demo products, customers, and sample stock. Clicking install runs migrations and seeders.

  5. Done

    You are signed in and can open the Admin dashboard or the POS cashier screen immediately.

What the wizard does under the hood

  • Runs php artisan migrate --force to create all tables.
  • Seeds permissions, roles, accounting chart, and languages.
  • Creates the first store (code MAIN), a terminal (“Front Counter”), company/receipt defaults, and your Admin user.
  • Applies the industry preset (pharmacy batches, supermarket scales, etc.).
  • Optionally seeds demo catalog and sample transactions.
  • Ensures public file storage (storage:link or directory fallback) and sets STORAGE_PUBLIC_MODE.
  • Marks the install complete: sets APP_INSTALLED=true in .env and writes storage/app/.installed.

Industry presets

IndustryWhat you get
RetailGeneral merchandise defaults — barcodes, variants, everyday shop settings.
PharmacyBatch & expiry focus, drug schedules, prescription-oriented defaults.
SupermarketWeighed items, scale / PLU barcode defaults, high-volume catalog habits.

After installing

  • Confirm cron is running if you use scheduled reports or automatic backups.
  • Turn on backups from Backup & restore.
  • On production, keep APP_DEBUG=false and use HTTPS.

Tips & best practices

  • Use a fresh, empty database. Installing into a DB that already has tables can conflict with migrations.
  • Match PHP to 8.3+ before opening /install. Older PHP versions fail the first wizard step.
  • Keep APP_URL exact. Wrong scheme or path breaks cookies, asset URLs, and redirects.
  • Build assets before upload if the server has no Node: run npm run build locally, then upload public/build.
  • One install = one company. Multiple shops are stores inside the same install.

Notes & reinstall

The installer locks itself after setup. Completion sets APP_INSTALLED=true and creates storage/app/.installed. Visiting /install again redirects away once installed.

Reinstall only on a disposable environment. You would need a fresh empty database, set APP_INSTALLED=false, remove storage/app/.installed, and clear related config cache — never do this on a live shop with real data.

Stuck? Common causes: wrong DocumentRoot, missing APP_KEY, DB credentials, or unwritable storage. See Troubleshooting.


Next: Getting started · Related: Settings · Updates & license · Backup & restore