ShiftWall

Installing it yourself. Every step, nothing skipped.

The install, start to finish.

Twenty minutes, most of it waiting on an upload. No terminal, no code — hosting-panel screens the whole way. Written against cPanel because that's what most hosting ships; Plesk, DirectAdmin and the rest have the same screens under different names (a file manager, a database page, a document-root setting, a cron page). Plain-VPS notes are marked where they differ.

Requires: PHP 8.3+ · MySQL or MariaDB · any web server you can point at a folder · HTTPS · full list

No hosting yet? I recommend On Budget Services shared hosting — cPanel, PHP 8.3, and every screen in this walkthrough matches it, because it's what I run.

0 · The one thing unlike a normal site

Every PHP site you've ever uploaded works like this: drop files in public_html/something/ and yoursite.com/something/ serves them. ShiftWall does not work that way, and this is the part that trips everybody up.

The folder you're about to upload contains about a dozen things — app, config, vendor, .env — and only one of them may be reachable from the web: the folder called public. Everything else must sit where a browser can't get to it, because .env holds your database password in plain text.

/home/YOURNAME/                    ← your hosting account's top level
├── public_html/                   ← your main site, untouched
└── shiftwall/                     ← the app goes here, OUTSIDE public_html
    ├── app/  config/  vendor/  .env   ← the web can't reach any of this
    └── public/                    ← your subdomain points HERE

If your host won't let you work above public_html, there's a safe fallback in the troubleshooting section.

1 · Set the PHP version

cPanel → MultiPHP Manager → select your subdomain → choose PHP 8.3 or newer.

8.3 is the hard minimum. If your host's menu doesn't offer it, stop and ask them — nothing else in this guide works without it. (VPS: install php8.3-fpm and the extensions from the requirements list.)

2 · Make the subdomain

cPanel → DomainsCreate A New Domain:

That last field is the whole trick. cPanel will suggest public_html/schedule — replace it with the path above. It's fine that the folder doesn't exist yet.

Then SSL/TLS Status → tick the new subdomain → Run AutoSSL, and turn on Force HTTPS Redirect. Staff will type passwords into this site; it must be HTTPS.

VPS: an Nginx site block instead —

root /home/YOU/shiftwall/public;
index index.php;
location / { try_files $uri $uri/ /index.php?$query_string; }
location ~ \.php$ { include fastcgi.conf;
  fastcgi_pass unix:/run/php/php8.3-fpm.sock; }

3 · Make the database

cPanel → MySQL Databases:

  1. Create New Database — name it shiftwall; cPanel makes it YOURNAME_shiftwall
  2. Add New User — pick a name, hit Password Generator, copy the password
  3. Add User To Database — tick ALL PRIVILEGES

Write down all three: database name, username, password. Both names get your account name stuck on the front — use the full versions, prefix and all.

4 · Upload

cPanel → File Manager — easier than FTP here, because it can unzip:

  1. Navigate to /home/YOURNAME — the level above public_html
  2. + Folder → name it shiftwall
  3. Go into it → Upload → pick the ShiftWall zip
  4. Back in File Manager, right-click the zip → Extract
  5. Delete the zip

One zip rather than loose files is deliberate: the app is roughly ten thousand small files, and FTP uploads them one at a time — half an hour, and it silently drops a few. One zip takes about a minute. If you must use FTP, upload the zip by FTP and still use File Manager for the extract.

5 · The settings file

The app needs a settings file that is deliberately not in the zip, because it will hold live passwords. In File Manager, inside shiftwall, find .env.example. (Can't see files starting with a dot? Settings, top right → tick Show Hidden Files.)

Right-click it → Copy → name the copy .env. Then right-click .envEdit, and set these lines:

APP_NAME="Your Store Name"
APP_ENV=production
APP_DEBUG=false
APP_URL=https://schedule.yourstore.com
APP_TIMEZONE=America/Chicago

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_DATABASE=YOURNAME_shiftwall
DB_USERNAME=YOURNAME_shiftwalluser
DB_PASSWORD=the-password-you-copied

SESSION_SECURE_COOKIE=true

INSTALL_TOKEN=paste-something-long-and-random-here

Save.

6 · Run the setup page

Go to:

https://schedule.yourstore.com/install/YOUR-INSTALL-TOKEN

— the last part being the exact random characters from your .env. You'll see a small page titled "Set up the schedule." It asks two things: the owner's name and the email they'll sign in with. Press Set it up, and in one go it generates the app's secret key, creates every database table, and creates the owner's account.

It deliberately creates no shifts and no jobs — your store isn't anybody else's. The first sign-in walks the owner through defining your own, in plain words, in about ten minutes.

The page then shows a password once. Copy the email and password somewhere before you close the tab — the password is scrambled the moment it's stored and can never be looked up again. (Lost it anyway? It's survivable — see troubleshooting.)

Then go back to .env and delete the INSTALL_TOKEN line. The page already refuses to run twice, but there's no reason to leave the key in the door.

If the page says "Can't reach the database"

.env and your panel disagree. Check both names carry your account prefix, the user was added with all privileges, and DB_HOST is 127.0.0.1.

If you get a 404

Either the token in the address doesn't exactly match the one in .env, or an account already exists — setup already ran, and you should just go to /login.

7 · Two cron jobs

Publishing a schedule queues the messages; a background task actually sends them. Without it, publishing looks fine and nothing ever arrives.

cPanel → Cron JobsAdd New Cron Job → Common Settings: Once Per Minute. Add both:

# sends the messages
cd /home/YOURNAME/shiftwall && /usr/local/bin/php artisan queue:work --stop-when-empty --max-time=55 >/dev/null 2>&1

# scheduled housekeeping
cd /home/YOURNAME/shiftwall && /usr/local/bin/php artisan schedule:run >/dev/null 2>&1

If messages still don't send, the PHP path differs on your host — on many cPanel servers it's /opt/cpanel/ea-php83/root/usr/bin/php. Swap that in.

8 · Email

Schedules go out by email, so the app needs a mailbox to send from. cPanel → Email Accounts → create something like schedule@yourstore.com, then add to .env:

MAIL_MAILER=smtp
MAIL_HOST=mail.yourstore.com
MAIL_PORT=465
MAIL_USERNAME=schedule@yourstore.com
MAIL_PASSWORD=the-mailbox-password
MAIL_ENCRYPTION=ssl
MAIL_FROM_ADDRESS=schedule@yourstore.com
MAIL_FROM_NAME="Your Store Name"

Test it by publishing a week to yourself before adding real staff. If it lands in spam: cPanel → Email Deliverability → fix SPF and DKIM. Usually one button each.

9 · First sign-in

Go to your new address, sign in with the email and one-time password from step 6, and do two things right away: change that password (Settings → Security) and turn on two-factor sign-in while you're there.

ShiftWall then greets you with its setup walk-through — your store's name, your jobs, your shifts, your people. From there the guide takes over.

! · When something goes wrong

A blank white page
Almost always a missing APP_KEY. Put your INSTALL_TOKEN line back in .env and visit the install address again — on a site that already has accounts, that page does exactly one thing: regenerates the key, touching no data. Delete the token line after. If the key is fine, set storage and bootstrap/cache permissions to 755 recursively; the real error is at the bottom of storage/logs/laravel.log.
A 500 error right after uploading
The document root points at the wrong folder. It must be the public folder inside the app — /home/YOURNAME/shiftwall/public — not the app folder itself and not public_html.
Signing in bounces you straight back to the login page
APP_URL doesn't match the address in your browser, exactly, https and all. Fix it; if nothing changes, also delete bootstrap/cache/config.php — a cached copy that wins over the file.
The page loads as plain unstyled text
The public/build folder didn't survive the upload, or APP_URL is wrong. Re-extract the zip and check both.
You edited .env and nothing changed
Delete bootstrap/cache/config.php if it exists.
Schedules never arrive
The queue cron isn't running — check the folder path and PHP path in step 7, then the mail settings in step 8.
You lost the owner password before writing it down
Add INSTALL_TOKEN back to .env, open your database's users table in phpMyAdmin and delete the owner's row, then visit the install address again — it makes a fresh owner account and shows a new password. Everything else stays put. Delete the token line when done.
Your host won't let you out of public_html
Put the app at public_html/shiftwall and point the subdomain's document root at …/public_html/shiftwall/public. That works, defended by the shipped .htaccess — but prefer the layout at the top when your host allows it. cPanel's File Manager can always go above public_html, even when FTP can't.
Updating later
Upload the new zip into the same folder, Extract over the top, delete the zip, leave .env alone. If an update needs database steps, its release notes say so.
Stuck anywhere on this page? That's what the email is for: carl@carl-prewitt.com. And if you'd rather skip all of it, I install ShiftWall for buyers on request.