This episode assembles a LEMP stack on Alpine: nginx as the web server, PHP-FPM for PHP applications, and MariaDB, PostgreSQL, or SQLite as the database. You'll learn installing via apk, configuring virtual hosts, and managing all the services with OpenRC.

One of Alpine's most common uses is running web applications. Episode 11 assembles the LEMP stack — Linux, nginx, MySQL/MariaDB, and PHP — entirely on Alpine with OpenRC managing the services.
Compared to other distros, building a LEMP stack on Alpine feels light and fast: all components are available in the main and community repositories, configuration is simple text, and the total footprint is much smaller. You'll build the complete stack from scratch until your first PHP application shows up in the browser.
Install nginx and enable it:
apk add nginx
rc-service nginx start
rc-update add nginx defaultThe main configuration files live in /etc/nginx/http.d/ and /etc/nginx/nginx.conf. Create your first virtual host:
server {
listen 80;
server_name app.example.com;
root /var/www/app;
index index.php index.html;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}Check the syntax then reload:
nginx -t
rc-service nginx reloadnginx -t validates the entire configuration before it's applied.
Alpine provides several PHP versions: php81, php82, php83, and php84. For the 3.24 release, use the newest one:
apk add php84-fpm php84-mysqli php84-opcache
rc-service php-fpm84 start
rc-update add php-fpm84 defaultThe FPM service listens on port 9000, matching the fastcgi_pass 127.0.0.1:9000 in the virtual host above. Create a test file:
mkdir -p /var/www/app
printf '%s\n' '<?php phpinfo(); ?>' > /var/www/app/index.php
chown -R nginx:nginx /var/www/appThen check the result:
curl -I http://localhost/
curl -s http://localhost/ | head -5The curl -I http://localhost/ output shows the HTTP headers; if the headers contain 200 OK, nginx and PHP-FPM are connected.
The database choice depends on your application's needs:
apk add mariadb mariadb-client
apk add postgresql16 postgresql16-client
apk add sqliteFor MariaDB, prepare the data directory then enable the service:
mysql_install_db --user=mysql --datadir=/var/lib/mysql
rc-service mariadb start
rc-update add mariadb default
mysql_secure_installationmysql_secure_installation locks down the fresh install: set a root password, remove anonymous users, and disable the test database. For PostgreSQL:
rc-service postgresql start
rc-update add postgresql default
su postgres -c "psql -c \"CREATE USER app WITH PASSWORD 'rahasia';\""
su postgres -c "psql -c \"CREATE DATABASE appdb OWNER app;\""The su postgres -c "psql ..." command runs psql as the postgres user, following PostgreSQL's convention.
The correct boot order affects the stack's reliability. Make sure the database starts first, then the application:
rc-update show
rc-statusAll services — mariadb, php-fpm84, and nginx — must be in the default runlevel. Test the stack end to end:
curl -s -o /dev/null -w "%{http_code}\n" http://localhost/index.php
mysql -u app -p -e "SELECT 1;"The curl -s -o /dev/null -w "%{http_code}\n" output shows the HTTP status code, for example 200. If you get 500, check the error logs:
tail -20 /var/log/nginx/error.log
tail -20 /var/log/php-fpm.logTip
Don't install several PHP versions at once — port conflicts and FPM services will confuse you. Pick one version and install extensions consistent with it, like php84-* for PHP 8.4.
Episode 11 assembled a complete LEMP stack on Alpine: nginx with virtual hosts, PHP-FPM with extensions, MariaDB and PostgreSQL as database options, and all services managed with OpenRC.
Key takeaways:
In the next episode, episode 12, we'll cover the kernel and kernel management — the difference between linux-lts and linux-stable, the uname command for verification, module management with modprobe and lsmod, and boot parameters via GRUB and efibootmgr.