Skip to main content

Deploy a PHP Stack (Apache + MySQL)

A minimal PHP app that counts page visits in MySQL, deployed as a Stack. Unlike the Python and Node tutorials (which inject a single connectionString), this one wires individual fields — host, user, password, database — which is the idiomatic shape for PHP/mysqli.

Result: visiting the app shows Hello from Kuploy Stacks! Visits: N, incrementing on each refresh.

1. The app​

index.php
index.php
<?php
// Each value is injected by a separate stack connection.
$db = new mysqli(
getenv('DB_HOST'),
getenv('DB_USER'),
getenv('DB_PASSWORD'),
getenv('DB_NAME')
);
$db->query("CREATE TABLE IF NOT EXISTS visits (count INT)");
$db->query("INSERT INTO visits (count) SELECT 0 WHERE NOT EXISTS (SELECT 1 FROM visits)");
$db->query("UPDATE visits SET count = count + 1");
$n = $db->query("SELECT count FROM visits")->fetch_row()[0];
echo "Hello from Kuploy Stacks! Visits: {$n}";

Grab this from kuploy/examples (subdir php-mysql), or create the files yourself and push them to a Git repo. First complete the tutorial Prerequisites — a connected Git provider and a build registry — then connect the repo and set the build path as in Deploy Your First App.

2. Choose a build method​

See Build Methods for the full list.

Recommended for this example. The repo includes a Dockerfile:

Dockerfile
FROM php:8.3-apache
RUN docker-php-ext-install mysqli
COPY . /var/www/html/
EXPOSE 80

When you create the app, set Build Type → Dockerfile, enter Dockerfile in the Docker File field (required; relative to the build path), and use port 80.

3. Create the services​

In your project's environment:

  1. Create Service → Application named web. The Set up your application wizard opens — walk the steps in order (or close it and use the service's tabs manually):
    • Source — point it at your Git repo (set Build Path php-mysql if using kuploy/examples).
    • Build — pick the Build Type from above (Dockerfile for this example).
    • Network — set the port to 80; add a domain if you want it public.
    • Registry — select the registry from the prerequisites; without it the build has nowhere to push and the deploy fails with Registry required. (Skipped automatically for Docker-image sources or single-server deploys.)
    • Deploy — stays disabled until source and registry are set. Don't deploy it yet.
  2. Create Service → Database → MySQL named db; note the database name, user, and password you set. The internal host is the service's app name (mysql://<user>:<password>@<db-app-name>:3306/<database>, on the db's General tab; see Databases) — the connections below inject host / user / password / database individually.

4. Group them into a stack​

  1. Environment toolbar → Stacks → Create stack, name it visits, open it.
  2. Add component → add web, then db.

5. Connect (four fields)​

Open Connect and create four connections, all with provider db → consumer web:

Source fieldEnv var name
hostDB_HOST
userDB_USER
passwordDB_PASSWORD
databaseDB_NAME

Each connection injects one value; together they give mysqli everything it needs. host resolves to the database's in-cluster service name, so traffic never leaves your private network.

6. Deploy​

Click Deploy stack — db deploys before web (it's the provider on all four connections). Open the app's domain and refresh; the counter climbs.

Verify the injection

The web service's Environment tab shows a managed block with DB_HOST, DB_USER, DB_PASSWORD, and DB_NAME resolved.

What you learned​

  • Connections can inject a full connectionString or individual fields (host/user/password/database) — use whatever your stack expects.
  • Multiple connections from the same provider are fine; the platform still deploys db first.

Next: the same app in Python or Node.js.