This is the development environment — a checkout you can run and change. For
installing FaPost on a server, see Self-hosting
instead; the two have different goals and different trade-offs.
With Docker
Nothing to install but Docker itself.http://localhost:8000, Vite on http://localhost:5173.
Why the dev image shares the production base
The development image is thedev target of docker/Dockerfile, built from the
same base stage as the production one. That is deliberate: a separately
assembled development image would have its own PHP build and extension set, and
every difference between the two becomes a bug that only appears after deployment.
What does differ is only what should:
- dev Composer dependencies are installed
- the source is mounted rather than copied, so edits are live
opcache.validate_timestamps=1, or nothing you type would take effect- the container runs as your user id, so files it writes stay yours
- Xdebug is available, off unless the image is built with
INSTALL_XDEBUG=1
vendor/ and node_modules/ are deliberately not mounted from the host.
They are installed inside the container for its own platform, and sharing them
would mix binaries built for a different OS.Xdebug
host.docker.internal, which resolves on Linux too
because compose maps it to the host gateway. If your IDE is elsewhere:
On your own machine
Requirements:- PHP 8.4 or newer
- Composer 2
- Node.js 20+ and npm
- PostgreSQL 15+ or compatible
- Redis
- PHP extensions:
pdo_pgsql,redis,intl,bcmath,gd,zip, pluspcntlandposixfor Horizon
1
Install dependencies
2
Prepare the environment
.env.example ships the credentials the Docker stack creates for itself, so
that path works unedited. Running against services you provisioned yourself
means pointing these at them:If your Redis runs without
requirepass, set REDIS_PASSWORD=null rather
than leaving it empty — Laravel reads the literal null as “no password”,
and an empty string is a password of zero length.3
Migrate and run
composer run dev starts the Laravel dev server, the queue listener, pail
log tailing, and the Vite dev server together.The Makefile
The repository root has aMakefile that wraps the everyday commands. It is a
front-end, not a second source of truth: where Composer holds a sequence
(setup, dev, test) the target delegates to it, so there is one definition
of each.
make with no arguments prints each target with its description and, under
it, the command that would actually run. Read that before running anything
unfamiliar: it also shows whether the command goes through a container.
Running commands inside a container
By default every target runs against whatever PHP is on yourPATH. If your
PHP lives in a container — devilbox, a Compose stack, anything docker exec
can reach — set CONTAINER and the PHP-side targets are prefixed with
docker exec automatically:
.make.local at the repository root.
The file is git-ignored: it describes your machine, not the project.
WORKDIR defaults to devilbox’s layout, /shared/httpd/<directory name>; set
it when your container mounts the checkout elsewhere. The container name comes
from Docker Compose: <project>-<service>-<index>, so devilbox’s PHP service is
server-php-1 and its PostgreSQL is server-pgsql-1.
Targets that run host tools stay on the host regardless of CONTAINER:
make dev (Vite needs the host’s Node), make docs-dev (Mintlify), the tunnel
targets. make shows which is which.
Adding packages on top of Core
To work against a package Core does not require — a Solution, a Plugin or a private extension — without touching Core’scomposer.json, use a composer overlay. See
Development setup → Packages Core does not require.
Checks
make test, make test-arch, make fix.
See Testing and architecture checks for what each covers.