A lightweight WordPress setup using SQLite instead of MySQL/MariaDB, powered by FrankenPHP. No database server required.
For a fresh Ubuntu 24.04 server:
curl -fsSL https://raw.githubusercontent.com/YOUR_REPO/setup.sh | sudo bash -s -- yourdomain.com 8000
Or clone and run:
git clone https://github.com/YOUR_REPO/wordpress-frankenphp-sqlite
cd wordpress-frankenphp-sqlite
sudo ./setup.sh yourdomain.com 8000
/var/www/yourdomain.com/
├── wp-config.php # WordPress config (outside document root)
├── database/ # SQLite database (not web-accessible)
│ └── wordpress.db
└── public/ # Document root
├── wp-admin/
├── wp-content/
├── wp-includes/
└── index.phpFollow these steps if you prefer to install manually or need to customize the setup.
Add the repository and install FrankenPHP:
# Add the static-php repository (provides FrankenPHP)
curl -fsSL https://deb.henderkes.com/gpg.key | sudo gpg --dearmor -o /usr/share/keyrings/static-php.gpg
echo "deb [signed-by=/usr/share/keyrings/static-php.gpg] https://deb.henderkes.com/ stable main" | \
sudo tee /etc/apt/sources.list.d/static-php.list
# Install FrankenPHP
sudo apt-get update
sudo apt-get install -y frankenphp
FrankenPHP uses a modular static PHP build. Install PDO and SQLite support:
sudo apt-get install -y php-zts-pdo php-zts-pdo-sqlite php-zts-sqlite3
Verify the extensions are loaded:
php -m | grep -i pdo
# Should output:
# PDO
# pdo_sqlite
WP-CLI is the command-line interface for WordPress:
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
chmod +x wp-cli.phar
sudo mv wp-cli.phar /usr/local/bin/wp
# Verify installation
wp --info
Set up the site directory following the Filesystem Hierarchy Standard:
export SITE_NAME="yourdomain.com"
sudo mkdir -p /var/www/${SITE_NAME}/public
sudo mkdir -p /var/www/${SITE_NAME}/database
sudo chown -R frankenphp:frankenphp /var/www/${SITE_NAME}/
Use WP-CLI to download WordPress:
sudo -u frankenphp wp core download --path=/var/www/${SITE_NAME}/public/
Download and install the official SQLite plugin:
# Note: We can't use `wp plugin install` here because WordPress tries to
# connect to MySQL before the SQLite plugin is active
curl -sLo /tmp/sqlite-database-integration.zip \
https://downloads.wordpress.org/plugin/sqlite-database-integration.zip
sudo unzip -q -o /tmp/sqlite-database-integration.zip \
-d /var/www/${SITE_NAME}/public/wp-content/plugins/
sudo chown -R frankenphp:frankenphp \
/var/www/${SITE_NAME}/public/wp-content/plugins/sqlite-database-integration
rm -f /tmp/sqlite-database-integration.zip
Copy the database drop-in file:
sudo cp /var/www/${SITE_NAME}/public/wp-content/plugins/sqlite-database-integration/db.copy \
/var/www/${SITE_NAME}/public/wp-content/db.php
sudo chown frankenphp:frankenphp /var/www/${SITE_NAME}/public/wp-content/db.php
Generate the configuration file using WP-CLI:
export SITE_URL="https://yourdomain.com:8000" # Adjust for your setup
# Create initial wp-config.php
sudo -u frankenphp wp config create \
--path=/var/www/${SITE_NAME}/public/ \
--dbname=wordpress \
--dbuser='' \
--dbpass='' \
--dbhost='' \
--skip-check
# Add SQLite configuration
sudo -u frankenphp wp config set DB_ENGINE sqlite --path=/var/www/${SITE_NAME}/public/
sudo -u frankenphp wp config set DB_DIR "/var/www/${SITE_NAME}/database/" --path=/var/www/${SITE_NAME}/public/
sudo -u frankenphp wp config set DB_FILE wordpress.db --path=/var/www/${SITE_NAME}/public/
# Set site URLs
sudo -u frankenphp wp config set WP_HOME "${SITE_URL}" --path=/var/www/${SITE_NAME}/public/
sudo -u frankenphp wp config set WP_SITEURL "${SITE_URL}" --path=/var/www/${SITE_NAME}/public/
For security, move the config file one level up:
sudo mv /var/www/${SITE_NAME}/public/wp-config.php /var/www/${SITE_NAME}/wp-config.php
Update ABSPATH since wp-config.php is now one level above WordPress:
sudo sed -i "s|define( 'ABSPATH', __DIR__ . '/' );|define( 'ABSPATH', __DIR__ . '/public/' );|" \
/var/www/${SITE_NAME}/wp-config.php
Add reverse proxy HTTPS handling:
sudo tee -a /var/www/${SITE_NAME}/wp-config.php << 'EOF'
// Handle reverse proxy HTTPS
if ( isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https' ) {
$_SERVER['HTTPS'] = 'on';
}
EOF
Key points about wp-config.php placement:
wp-config.php in its own directoryABSPATH is set to __DIR__ . '/public/' because the config file is now one level above WordPresswp-config.php (which contains secrets) outside the web-accessible directory# Set ownership to FrankenPHP user
sudo chown -R frankenphp:frankenphp /var/www/${SITE_NAME}/
# Secure the database directory (only frankenphp and root can access)
sudo chmod 750 /var/www/${SITE_NAME}/database
# Ensure wp-content is writable for uploads, plugins, themes
sudo chmod -R 755 /var/www/${SITE_NAME}/public/wp-content
Edit /etc/frankenphp/Caddyfile:
export PORT=8000
sudo tee /etc/frankenphp/Caddyfile << EOF
{
frankenphp
}
:${PORT} {
root /var/www/${SITE_NAME}/public/
encode zstd br gzip
php_server
}
EOF
FrankenPHP’s systemd service uses ProtectSystem=full which makes /var read-only by default. We need to allow writes to specific directories:
sudo mkdir -p /etc/systemd/system/frankenphp.service.d/
sudo tee /etc/systemd/system/frankenphp.service.d/override.conf << EOF
[Service]
ReadWritePaths=/var/www/${SITE_NAME}/public/wp-content
ReadWritePaths=/var/www/${SITE_NAME}/database
EOF
sudo systemctl daemon-reload
sudo systemctl enable frankenphp
sudo systemctl restart frankenphp
Verify it’s running:
sudo systemctl status frankenphp
curl -I http://localhost:8000/
Visit your site in a browser:
http://yourdomain.com:8000/You’ll see the WordPress installation wizard. Fill in your site details.
Complete the installation entirely from the command line:
sudo -u frankenphp wp core install \
--path=/var/www/${SITE_NAME}/public/ \
--url="${SITE_URL}" \
--title="My Site" \
--admin_user=admin \
--admin_password="YourSecurePassword123!" \
--admin_email=admin@example.com
Common WP-CLI commands for managing your site:
# Always run as frankenphp user with the correct path
export WP="sudo -u frankenphp wp --path=/var/www/yourdomain.com/public/"
# Update WordPress core
$WP core update
# Update all plugins
$WP plugin update --all
# Update all themes
$WP theme update --all
# Install and activate a plugin
$WP plugin install jetpack --activate
# Install and activate a theme
$WP theme install flavor --activate
# Create a new user
$WP user create editor editor@example.com --role=editor
# Search and replace (useful when migrating)
$WP search-replace 'old-domain.com' 'new-domain.com'
# Export database (creates SQL-like output even for SQLite)
$WP db export backup.sql
# Clear caches
$WP cache flush
Install the missing extensions:
sudo apt-get install -y php-zts-pdo php-zts-pdo-sqlite php-zts-sqlite3
sudo systemctl restart frankenphp
The systemd service is blocking writes. Check the override:
cat /etc/systemd/system/frankenphp.service.d/override.conf
Ensure ReadWritePaths includes both wp-content and database directories.
Ensure the frankenphp user owns the database:
sudo chown frankenphp:frankenphp /var/www/yourdomain.com/database/wordpress.db
Always run WP-CLI as the frankenphp user:
sudo -u frankenphp wp --path=/var/www/yourdomain.com/public/ <command>
sudo journalctl -u frankenphp -f
Database location: The SQLite database is stored outside the document root in /var/www/site/database/, making it inaccessible via web requests.
wp-config.php location: Also outside the document root, protecting your authentication keys and database credentials.
Directory permissions: The database directory has mode 750, readable only by the frankenphp user and root.
HTTPS: If you’re behind a reverse proxy that terminates SSL, the config includes handling for X-Forwarded-Proto headers.
To backup your entire site:
tar czf backup-$(date +%Y%m%d).tar.gz /var/www/yourdomain.com/
The SQLite database is just a single file, making backups simple:
cp /var/www/yourdomain.com/database/wordpress.db wordpress-backup-$(date +%Y%m%d).db
Or use WP-CLI:
sudo -u frankenphp wp db export backup.sql --path=/var/www/yourdomain.com/public/
This setup is extremely lightweight:
For even lower resource usage, you can configure systemd to:
This is ideal for low-traffic sites or development environments.
See SOCKET_ACTIVATION.md for detailed setup, or use the quick install below.
# Stop the always-on service
sudo systemctl stop frankenphp
sudo systemctl disable frankenphp
# Install socket activation units
sudo cp frankenphp.socket /etc/systemd/system/
sudo cp frankenphp-socket.service /etc/systemd/system/frankenphp.service
sudo cp frankenphp-idle.service /etc/systemd/system/
sudo cp frankenphp-idle.timer /etc/systemd/system/
# Update Caddyfile for socket activation
sudo cp Caddyfile.socket-activated /etc/frankenphp/Caddyfile
# Edit /etc/frankenphp/Caddyfile to set your site root
# Enable socket and idle timer
sudo systemctl daemon-reload
sudo systemctl enable --now frankenphp.socket
sudo systemctl enable --now frankenphp-idle.timer
Request → Socket (always listening) → Starts Service → Handles Request
↓
Timer checks every N minutes
↓
No connections? Stop service.
↓
Next request? Socket restarts it.# Socket listening, service stopped
ss -tlnp | grep 8000 # Port open
sudo systemctl is-active frankenphp.service # inactive
# Make a request
curl http://localhost:8000/
# Service now running
sudo systemctl is-active frankenphp.service # active
# Wait for idle timer (default: 1 min for testing, 5 min for production)
# Service will stop automatically
Edit /etc/systemd/system/frankenphp-idle.timer:
[Timer]
OnBootSec=5min
OnUnitActiveSec=5min # Check every 5 minutes
Then reload: sudo systemctl daemon-reload && sudo systemctl restart frankenphp-idle.timer
MIT