Getting Started

cPanel Hosting & Cron

Deploying to a typical shared cPanel account — file layout, database setup, the included .htaccess, and the cron jobs the scheduler and queue need to actually run.

Applies to any shared/cPanel host

This page walks through deploying MoneyMate on a typical cPanel shared-hosting account (the most common target for a CodeCanyon buyer without their own server). If you have root access to your own server instead — a DigitalOcean Droplet or similar — see VPS Hosting (DigitalOcean & Apache) instead, which provisions the whole stack from a blank server.

1. Upload the application

Two supported layouts, depending on what your host lets you do with the document root — decide which applies to you before you start uploading, since it determines where you extract to:

Option A — document root set to /public

If your host lets you point the domain's document root directly at the project's public folder (common on VPS-style cPanel or reseller setups), extract the project anywhere outside the web root and set the domain's document root to its public subfolder. This is the cleanest option — no .htaccess redirect needed.

Option B — fixed document root (most shared hosting)

Most shared cPanel accounts fix the document root to public_html (or an add-on domain's folder) and won't let you change it. Extract the entire project into that folder as-is. The root .htaccess file already included in this project routes every request into its own public subfolder automatically — see the callout below.

Uploading and extracting via File Manager (recommended)

Uploading the single zip archive and extracting it on the server is dramatically faster than uploading thousands of individual files over FTP — do this even if you're more comfortable with an FTP client normally.

  1. Open File Manager

    Log into cPanel, and under the Files section click File Manager. Navigate to where you're extracting to — public_html for Option B, or wherever you're placing the project for Option A (e.g. a folder one level above public_html, outside the web root).

  2. Upload the zip

    Click Upload in the top toolbar. A new tab/panel opens — either drag the zip archive onto it or click Select File and browse to it on your computer. Wait for the progress bar to reach 100% (the archive is roughly 40–45MB, so this takes anywhere from a few seconds to a couple of minutes depending on your connection), then go back to the File Manager tab.

  3. Extract it

    Refresh the file list if the zip doesn't appear yet. Right-click the uploaded .zip file and choose Extract from the context menu (or select it and click Extract in the toolbar). cPanel asks for a destination — the current folder is correct in almost every case — then extracts everything in place, right there on the server.

  4. Confirm the dotfiles came through

    The archive's top level has no wrapping folder — app/, artisan, composer.json, .env.example, and .htaccess all land directly in the folder you extracted into, not inside a nested moneymate/ subfolder. File Manager hides dotfiles by default: click Settings (top-right corner) and enable Show Hidden Files (dotfiles), then confirm .htaccess and .env.example are both actually there.

  5. Delete the zip

    Once you've confirmed the extraction, delete the .zip file itself — no reason to keep a second copy of everything counting against your disk quota.

Alternative: SFTP

If you'd rather use a desktop client (FileZilla, Cyberduck, Transmit), connect over SFTP — not plain FTP, which sends your cPanel password unencrypted:

FieldValue
HostYour domain, or the server hostname/IP from your welcome email
ProtocolSFTP — SSH File Transfer Protocol
Port22 (some hosts use a non-standard port — check your welcome email)
Username / PasswordYour cPanel username and password

Even over SFTP, upload just the zip archive first and extract it through File Manager as above (step 3) rather than dragging the extracted project's ~12,000 individual files through the FTP client — the difference is minutes versus potentially hours.

The included root .htaccess

A .htaccess file at the project root ships with this application specifically for Option B. It rewrites every request that isn't already under /public/ into the public folder, whose own .htaccess then hands it to Laravel's front controller — so a fixed public_html document root works exactly like a real /public document root. You don't need to write or edit this file yourself; just make sure it uploaded along with everything else (see step 4 above — dotfiles are hidden or skipped by some zip tools/FTP clients by default).

2. Create a MySQL database

  1. MySQL Databases

    In cPanel, open MySQL® Databases. Create a new database, then create a new database user with a strong password.

  2. Add the user to the database

    Under "Add User to Database", grant the new user ALL PRIVILEGES on the database you just created.

  3. Note the details down

    cPanel typically prefixes both the database name and username with your cPanel account name (e.g. cpaneluser_moneymate) — you'll enter the full prefixed names into the installer.

3. Set the PHP version

Open MultiPHP Manager in cPanel and set your domain to PHP 8.3 or higher. While there, open MultiPHP INI Editor for the same domain and confirm upload_max_filesize, post_max_size, and memory_limit are reasonably generous (128M+ is comfortable) — the defaults on some hosts are too low for attachment uploads and CSV imports.

4. Folder permissions

Laravel needs to write to two folders. In File Manager (or via SFTP), set:

storage/ ................ 755 (recursively)
bootstrap/cache/ ......... 755 (recursively)

If your host's PHP runs as a different user than your FTP/File Manager user, you may need 775 instead — if the installer or application later shows a "permission denied" writing to storage/logs, this is the first thing to check.

This same mismatch can also stop the installer from creating the public/storage symlink (a one-time write into public/, not storage/) — on cPanel this is rare since File Manager and PHP normally share one account user, but if you uploaded via a different system account, the installer completes anyway (an admin notice will say so) and you just run php artisan storage:link once via Terminal or an SSH session afterward.

5. Enable SSL

Under SSL/TLS Status or AutoSSL, issue a free Let's Encrypt certificate for your domain before running the installer — Stripe webhooks and secure session cookies both expect HTTPS in production.

6. Run the installer

Visit your domain in a browser and follow the Installation & Setup wizard using the database credentials from step 2.

7. Set up the cron job (required)

MoneyMate's scheduled tasks — daily budget-threshold checks, due-date reminders, recurring-transaction processing, net worth snapshots, the weekly digest email, and package grace-period sweeps — all run through Laravel's scheduler. On a real server this needs exactly one cron entry, which then dispatches everything defined in routes/console.php at its own configured time.

  1. Open Cron Jobs

    In cPanel, open the Cron Jobs tool.

  2. Add a new cron job

    Set it to run every minute (Common Settings won't offer this — set each field to * manually), and use this command, substituting your real account path and PHP version:

    * * * * * /usr/local/bin/php83 /home/cpaneluser/public_html/artisan schedule:run >> /dev/null 2>&1

    Replace /usr/local/bin/php83 with your host's PHP 8.3+ CLI binary (cPanel hosts typically expose versioned binaries like php83/php84 under /usr/local/bin — check with your host if unsure, or run which php83 in cPanel's Terminal). Replace the path with wherever your project's artisan file actually lives — if you used Option B above, that's the same folder as public_html; if Option A, it's one level above your document root.

Every minute, not once a day

The cron entry itself runs every minute — that's normal and expected. Laravel's scheduler checks on each invocation whether any of the tasks in routes/console.php are actually due, and only runs the ones that are (most are configured ->daily() or ->weeklyOn()). Running the cron entry less often than every minute means those tasks can fire late or be silently skipped.

8. Queue worker (recommended)

Notification emails (budget alerts, due reminders, the weekly digest, AI-action completion) are queued jobs, not sent inline, so a real request never waits on an outgoing email. On a normal server this means a persistent php artisan queue:work process — most shared cPanel accounts don't allow long-running background processes, so use a second cron entry instead:

* * * * * /usr/local/bin/php83 /home/cpaneluser/public_html/artisan queue:work --stop-when-empty --max-time=50 >> /dev/null 2>&1

--stop-when-empty exits as soon as the queue is drained instead of waiting indefinitely, and --max-time=50 is a safety net that stops it before the next minute's cron entry starts a new one on top of it.

Simpler fallback: process jobs immediately, no queue at all

If your host doesn't comfortably support a second cron entry, set QUEUE_CONNECTION=sync in your .env file instead (then run php artisan config:clear). Every queued job then runs immediately, inline, the moment it's dispatched — simpler to operate, at the cost of the triggering request waiting slightly longer whenever an email is sent.

After launch

  • Confirm the cron ran at least once by checking Site Settings → Activity Log or your mail server logs after a few minutes.
  • Send yourself a test budget-threshold alert or use the "Send test email" action in SMTP Settings to confirm mail delivery end-to-end.
  • If Stripe is configured, add your webhook endpoint (https://yourdomain.com/stripe/webhook) in the Stripe Dashboard so subscription events reach the application.