Skip to main content

Get FrankPHP Running

By the end of this guide you’ll have FrankPHP installed on a subdomain, configured with a standard .env file, be logged in as a real user, and have built your first protected page. That’s enough to know the framework is working and to get a feel for how it thinks. Your host needs PHP 8.x and MySQL. Nothing else. No Composer, no Node, no build tools.

Step 1: Create your MySQL database

Log in to your hosting control panel and create a new, empty MySQL database. Most shared hosts do this through phpMyAdmin or a dedicated “MySQL Databases” section (cPanel, Plesk, etc.). Note down the following — you’ll need them in the next step:
  • Database name
  • Database username
  • Database password
  • Host (almost always localhost on shared hosting)

Step 2: Configure FrankPHP locally with .env

Download the latest release from FrankPHP’s GitHub Releases page — grab the Source code zip (or git clone the repo directly). Either way you’ll get a single project folder containing both framework/ and app/ as siblings — a complete working starting point. FrankPHP uses a standard .env file for environment-specific credentials. This keeps secrets out of your main config structure and makes FrankPHP feel more familiar if you’ve used other PHP frameworks before. Copy app/env.example to app/.env. The example file documents every key the application supports — including mail credentials needed for password reset — so it’s the safer starting point than creating one from scratch. Open app/.env and add your database credentials and core app settings:
The .env file is for environment-specific settings only. Keep framework defaults and non-sensitive application settings in the normal config structure (app/Config/config.php). Use .env for things like database credentials, mail credentials, and other secrets.
Do not upload your local .env to a public web root. It lives at app/.env, outside the browser-accessible app/public/ folder.

Step 3: Set up your directory structure on the server

Connect to your hosting account via FTP and navigate to your root directory. Create a project folder — name it whatever makes sense for what you’re building (your product’s own name, or just /frankphp). This guide uses /yourproject.
/www and /docs are optional and outside the scope of this guide — they’re here to show how a typical FrankPHP-based project organizes a hosting account. What matters right now is creating /yourproject.
Some FrankPHP-based products also add a /platform folder — a super-user layer that sits above individual tenant admins. This is a newer, still-emerging pattern and not yet part of the core framework release. Mention it here only if it’s relevant to what you’re building; most projects won’t need it yet.

Step 4: Upload FrankPHP into your project directory

Upload the entire contents of your downloaded FrankPHP folder into /yourproject. You should end up with two sibling folders inside it:
framework/ is not yours to edit. It’s replaced wholesale on every framework update, so any changes you make inside it will be silently lost the next time you update. All of your own code, views, and configuration live in app/ — see framework/codebase.md for the full framework/application ownership boundary.
Check: app/bootstrap.php and framework/bootstrap.php should each sit at the root of their own folder, not inside a subfolder. Your .env file should sit at app/.env.

Step 5: Point your subdomain to the public folder

This is the most important configuration step, and it’s worth understanding why. FrankPHP’s web root is app/public/ — a subdirectory inside app/, itself a subdirectory of /yourproject. This is the only folder your subdomain should point to. Your hosting control panel will have a setting for “Document Root” or “Web Root” when you create the subdomain — set it to:
So your full structure looks like this:
Why this matters: By pointing your domain to app/public only, your .env file and all framework/application source files stay outside the web root. That means your credentials cannot be requested directly in the browser. This is a simple, effective security layer and follows the same broad approach used by larger PHP frameworks.
Once the subdomain is configured, point app.yoursite.com to /yourproject/app/public.

Step 6: Let FrankPHP initialise the database

Nothing to do here by hand. The first time you visit the site, FrankPHP checks whether its core tables exist and, if not, creates them automatically from framework/sql/schema.core.sql and seeds two starter accounts so you can log in immediately.
If you’d rather review the schema before your first visit, you can still import framework/sql/schema.core.sql manually through phpMyAdmin’s Import tab, or by pasting its contents into the SQL command window. This is entirely optional — FrankPHP creates it automatically either way.

Step 7: Open FrankPHP in your browser

Navigate to https://app.yoursite.com in your browser. You should see the FrankPHP login screen.
If you see a login form, FrankPHP is installed correctly. Everything worked.
If you see a blank page or a PHP error, the most common causes are:
  • .env has incorrect database credentials
  • The subdomain is pointing to /yourproject/app instead of /yourproject/app/public
  • Your database user doesn’t have permission to create tables (needed for the automatic first-boot schema setup)
  • File permissions — your host may require folders to be 755 and files 644

Step 8: Log in

The schema seeds two accounts inside Tenant 1 — one owner-level, one standard user: Log in with the owner account. After login you’ll land on the default dashboard at:
“Forgot my password” is visible on the login screen but will not work yet — FrankPHP needs email credentials configured before it can send password reset links. When mail support is enabled, these credentials should also live in .env.
Change these passwords before you share the URL with anyone. They are seed credentials for development only.

Step 9: Build your first page

FrankPHP is running. Now let’s prove it properly by building something — a simple protected page that only a logged-in user can see. This touches every layer of the framework in the most straightforward way possible.

9a. Register the route

Open app/bootstrap.php and find the route definitions. Add one new line inside the existing authenticated routes block:
This tells the router: when a GET request comes in for this URL, run HelloController@index, and run the Tenant and Auth middleware first.

9b. Create the controller

Create a new file: app/Controllers/HelloController.php

9c. Create the view

Create a new file: app/Views/hello/index.php

9d. Upload and visit

Upload just the two new files via FTP — no need to re-upload the whole framework. Then visit:
You should see your new page, inside the app shell, with your user’s name and tenant displayed.
If you can see the page, you’ve just built your first FrankPHP feature. The route, middleware, controller, and view are all working together correctly.

What just happened

It’s worth pausing to understand what FrankPHP did when you visited that URL:
  1. app/public/index.php loaded framework/bootstrap.php, which immediately called Env::load() to read app/.env into $_ENV before anything else ran
  2. The database connection was established using credentials from $_ENV, framework container bindings and framework routes were registered, then framework/bootstrap.php hard-required app/bootstrap.php as its final step to register your application’s own routes and bindings — returning the router
  3. The router dispatched the request, matched /tenant/1/hello, and identified your controller
  4. TenantMiddleware ran first — it read 1 from the URL, loaded the tenant from the database, and attached it to the request
  5. AuthMiddleware ran second — it checked your session, loaded your user, confirmed they belong to tenant 1, and attached them to the request
  6. HelloController@index received the request with $request->tenant and $request->user already populated
  7. The view rendered inside app-main.php, the authenticated layout
No magic. Every step is explicit and traceable. That’s the FrankPHP way.

Why .env is worth using

Moving credentials into .env gives FrankPHP a cleaner separation between code and secrets. It also brings three practical benefits:
  1. Better security — credentials live outside the public web root and outside your main config file
  2. Cleaner deployments — each environment can have its own .env without editing framework code
  3. More familiar workflow — developers coming from Laravel, Symfony, and other PHP frameworks will immediately understand the pattern
This keeps FrankPHP aligned with common modern PHP practices while still staying true to the framework’s philosophy: no dependencies, no hidden machinery, and no complex setup pipeline.

Next steps

  • Add a form — learn how to handle POST requests and save data to the database
  • Understand middleware — see the full middleware cheatsheet and when to use each combination
  • Start your application — work through Working with MYAPP.md to record your app’s name, tables, and routes as you build
  • Configure version management — use the release tooling to track versions, changes, and packaged framework-only updates