Skip to content
Developer docs

Installing for development

Updated 5 min read

How to run Basehim on your own computer, what the server needs in detail, and how to set up Apache, nginx and a real cron job. Site owners installing on a hosting account want How to install Basehim on your hosting instead.

Requirements in detail

MinimumNotes
PHP8.18.2 or 8.3 recommended
PHP extensionspdo_mysql, mbstring, json, openssl, fileinfogd (smaller image sizes) and curl (updates, the marketplace, apps that call other servers) are recommended: the installer flags them but installs without them
DatabaseMySQL 5.7+ or MariaDB 10.3+One empty database and a user with full rights on it
Web serverApache or LiteSpeed with mod_rewritenginx works with the rules below
PHP command lineoptionalLets Basehim test-load an app in a separate process before it runs on your site (see When an app fails). Found automatically on most hosts; proc_open must not be disabled
Diskabout 30 MBplus your uploads

The installer checks all of this before it asks anything else, and says how to fix anything that is missing.

On your own machine

git clone https://github.com/basehim/basehim.git
cd basehim
php -S localhost:8000 server.php

Then open http://localhost:8000 and run the installer. Use 127.0.0.1 rather than localhost as the database host if PHP can't find the MySQL socket.

server.php sends requests to index.php and serves the asset folders, like .htaccess does on Apache. It only works under php -S.

Tip: PHP's built-in server handles one request at a time. That is fine for development. But an app that calls its own site over HTTP will wait on itself forever. Call core in-process through $this->api() instead (see The App API).

Files and folders the installer writes

The site folder must be writable while the installer runs, because it creates .env there. storage/ and content/ must stay writable afterwards (775 is enough; never use 777).

After the install, .env holds the database details, APP_URL, two random secrets (APP_KEY and JWT_SECRET) and INSTALLED=true. Anything your host had already put in it, such as TRUSTED_PROXIES, is kept. Keep a copy somewhere safe, and never put it in a public repository. The settings you can add are in Configuration.

Choosing the table prefix

There is nothing to set up. Since 1.2.70 the installer gives every site its own random table prefix, such as k1y_, and checks that no table in the database already uses it. Every table, including the ones apps create, gets that prefix, so any number of sites can share one database without touching each other's tables. Going back and retrying the installer reuses the prefix it chose the first time.

To choose the prefix yourself, put DB_PREFIX=site2_ in .env before running the installer; a prefix already in .env always wins. Never change it afterwards: the site would look for tables that don't exist.

Subfolder installs

Basehim detects its base path on every request, so a subfolder needs no configuration. Themes and apps should build links with $base, link_to(), theme_asset() or $this->asset() so they keep working there.

Apache

The bundled .htaccess does three jobs:

  • sends every request that isn't a real file to index.php;
  • passes the Authorization header through to PHP, which bearer tokens need on many cPanel servers;
  • denies direct access to app/, config/, database/, storage/ (except storage/uploads), routes/, tests/, bootstrap.php, .env and other dotfiles.

Keep it. If API calls with a valid key return 401, the Authorization header is being stripped: make sure .htaccess is uploaded and mod_rewrite is on.

nginx

nginx ignores .htaccess, so you have to repeat its rules yourself. Basehim is developed and tested on Apache; this server block mirrors the bundled .htaccess. Adjust root and the PHP-FPM socket:

server {
    listen 80;
    server_name example.com;
    root /var/www/basehim;
    index index.php;

    # Public assets are served as files.
    location ^~ /storage/uploads/ { try_files $uri =404; }
    location ^~ /admin/assets/    { try_files $uri =404; }
    location ~ ^/content/themes/[^/]+/assets/ { try_files $uri =404; }

    # Never serve these directly.
    location ~ ^/(app|config|database|storage|routes|tests)(/|$) { deny all; }
    location ~ ^/(bootstrap\.php|server\.php)$ { deny all; }
    location ~ /\. { deny all; }

    # Everything else goes to the front controller.
    location / { try_files $uri /index.php?$query_string; }

    location ~ \.php$ {
        # Only index.php and install.php are entry points.
        location ~ ^/(?!index\.php|install\.php) { return 404; }
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param HTTP_AUTHORIZATION $http_authorization;
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    }
}

The \.php$ rule refuses PHP files other than the two entry points. That includes app and theme files, which Apache would otherwise run directly.

Background tasks and cron

Apps can schedule work (hourly imports, daily clean-ups). By default due tasks run after a page has been sent to a visitor, so nobody waits for them. The catch: a site with no traffic runs nothing.

For tasks that must run on time, point a real cron job at the scheduler. The URL contains a secret token. Get it from the API as an administrator: with an API key that has the settings:read scope, or with a JWT.

curl -s https://example.com/api/v1/schedule \
  -H "Authorization: Bearer basehim_your_key" | grep -o '"cron_url":"[^"]*"'

Then add a crontab line (cPanel: Cron Jobs):

* * * * * curl -s "https://example.com/api/v1/schedule/run?token=YOUR_TOKEN" > /dev/null

A wrong token gets a plain 404, so the endpoint stays hidden.

More troubleshooting

SymptomLikely cause and fix
Every page except the home page is 404 on nginxAdd the try_files rule above.
API calls with a valid key get 401The server strips Authorization. Keep the bundled .htaccess; on nginx pass HTTP_AUTHORIZATION.
"Not installed yet" from /api or /mcp.env is missing. Open /install.php.
White page or "Something went wrong"Set APP_DEBUG=true in .env briefly, or read storage/logs/basehim-YYYY-MM-DD.log.
Links or images point to http:// on an HTTPS siteSet APP_URL=https://… in .env, and TRUSTED_PROXIES behind a proxy (see Configuration).
Scheduled tasks never runNo traffic and no cron. Add the cron line above.

Related: Configuration and the .env file, Updates, migrations and backups, When an app fails.

Discussion

Leave a comment