# 🐘 PHP Web Server β€” Alpine LAMP Stack ### ⚑ Lightweight Β· πŸ”’ Secure Β· πŸš€ Production-Ready > A complete PHP development environment packed into a **single Docker container** β€” Apache, PHP 8+, MariaDB, phpMyAdmin, File Manager & Web Terminal. > Deploy on **Hugging Face Spaces** for free, or on any **VPS / local machine**.

πŸ“‹ Table of Contents


✨ Features

Feature Description
🐘 PHP 8+ With OPcache pre-enabled β€” runs 2x–3x faster out of the box
🌐 Apache Configured with mod_rewrite and .htaccess support
πŸ—„οΈ MariaDB Full database engine with phpMyAdmin UI at /sql
πŸ“ File Manager TinyFileManager at /files
πŸ’» Web Terminal Custom browser-based shell at /terminal β€” no SSH needed
βš™οΈ .env Support Auto-loads /var/www/localhost/htdocs/.env at startup
πŸ’Ύ Persistent Storage /data mount required β€” database is stored here
πŸ”’ Non-root Runs as user 1000 for improved security
🐳 Alpine Base Ultra-lightweight image with minimal footprint
☁️ HF Spaces Ready Works seamlessly on Hugging Face Docker Spaces
πŸ”€ Custom Domain Mask your HF URL with a custom domain via Cloudflare Workers

πŸ“¦ What’s Inside

Alpine Linux (latest)
β”œβ”€β”€ Apache 2           β†’ Web server (port 7860)
β”œβ”€β”€ PHP 8.4
β”œβ”€β”€ MariaDB            β†’ Database server
β”œβ”€β”€ phpMyAdmin         β†’ Database UI at /sql
β”œβ”€β”€ TinyFileManager    β†’ File manager at /files
β”œβ”€β”€ Web Terminal       β†’ Custom shell UI at /terminal
β”œβ”€β”€ Composer           β†’ PHP dependency manager
β”œβ”€β”€ Git, Nano, Wget, Zip, Unzip, ImageMagick

🐘 PHP Extensions

Extension Purpose
php-mysqli MySQL / MariaDB direct connection
php-pdo PHP Data Objects β€” database abstraction base
php-pdo_mysql PDO driver for MySQL / MariaDB
php-pdo_pgsql PDO driver for PostgreSQL
php-mbstring Multibyte string handling β€” required by most frameworks
php-xml XML parsing and generation
php-simplexml Simple XML object interface β€” used by WordPress and APIs
php-dom Full DOM XML/HTML parsing β€” required by Laravel and Symfony
php-xmlwriter Writing XML documents programmatically
php-xmlreader Streaming XML reader for large files
php-xsl XSLT transformations
php-gd Image creation and manipulation (resize, crop, watermark)
php-imagick Advanced image processing via ImageMagick
php-exif Read image metadata (camera, GPS, dimensions)
php-curl HTTP requests β€” required by APIs, Guzzle, SDKs
php-session Session management
php-opcache Bytecode caching β€” makes PHP 2x–3x faster
php-phar PHP Archive support β€” required by Composer
php-openssl SSL/TLS encryption, JWT, secure hashing
php-sodium Modern cryptography library
php-iconv Character encoding conversion
php-mbstring Multibyte / Unicode string support
php-json JSON encode/decode
php-zip Create and extract ZIP archives
php-bz2 Bzip2 compression support
php-intl Internationalization β€” dates, currencies, locales
php-gettext Translations and i18n support
php-bcmath Arbitrary precision math β€” required by payment gateways
php-gmp GNU Multiple Precision β€” cryptography and big numbers
php-apcu In-memory user cache β€” speeds up repeated operations
php-redis Redis cache and session driver
php-soap SOAP web services client and server
php-ldap LDAP authentication and directory services
php-ctype Character type checking functions
php-fileinfo Detect file MIME types
php-tokenizer PHP code tokenizer β€” required by Composer and Laravel
php-sockets Low-level socket programming and WebSocket support
php-posix POSIX process functions
php-pcntl Process control β€” fork, signals, process management
php-ftp FTP client functions
php-calendar Calendar and date conversion functions
php-shmop Shared memory read/write
php-sysvmsg System V message queues
php-sysvsem System V semaphores
php-sysvshm System V shared memory
php-tidy HTML cleanup and repair
php-readline Interactive PHP shell (php -a) support

☁️ Deploy on Hugging Face Spaces

⭐ Recommended β€” Free hosting, no server required!

Step 1 β€” Create a New Space

  1. Go to huggingface.co/spaces
  2. Click β€œCreate new Space”
  3. Give your space a name (e.g. my-php-server)
  4. Select SDK β†’ Docker
  5. Set the Space visibility to Public
  6. Click β€œCreate Space”

⚠️ Keep your Space set to Public. Hugging Face requires the Space to be public for it to run continuously and be accessible via a URL. Private Spaces may sleep or become inaccessible depending on your plan.

Step 2 β€” Upload the Dockerfile

Only the Dockerfile is needed in your Space repository:

your-space/
└── Dockerfile    βœ… this is the only file needed here

πŸ’‘ Drag & drop the Dockerfile in the Files tab, or push via Git:

git clone https://huggingface.co/spaces/YOUR_USERNAME/YOUR_SPACE_NAME
cd YOUR_SPACE_NAME

# Only add the Dockerfile here
git add Dockerfile
git commit -m "Add Dockerfile"
git push

⚠️ Do NOT place your project files here.
Once the Space is live, upload your project files using the File Manager at /files.
Simply go to /files β†’ navigate to /var/www/localhost/htdocs β†’ upload your files there.

Step 3 β€” Set Environment Variables

On Hugging Face, environment variables are configured in Space Settings β€” not in the command line.

  1. Open your Space β†’ click β€œSettings” tab
  2. Scroll to β€œVariables and Secrets”
  3. Add these variables:
Variable Default Description
MYSQL_USER admin Database username
MYSQL_PASSWORD admin Database password (change this!)
MYSQL_DATABASE admin Database name
SQL_PATH sql URL path for phpMyAdmin β€” e.g. mysecretdb opens at /mysecretdb
FILES_PATH files URL path for File Manager
TERMINAL_PATH terminal URL path for Web Terminal

Step 4 β€” Mount Persistent Storage 🚨

This step is mandatory β€” without it, your database will reset on every restart.

Go to Space Settings β†’ Persistent Storage and add:

Full setup details β†’ Persistent Storage section

Step 5 β€” Done! πŸŽ‰

Hugging Face will build and deploy automatically. Your live URL:

https://YOUR_USERNAME-YOUR_SPACE_NAME.hf.space/

πŸ–₯️ Deploy on VPS / Local Machine

For those who prefer their own server or want to test locally.

Prerequisites

Step 1 β€” Clone & Build

git clone https://github.com/YOUR_USERNAME/YOUR_REPO_NAME.git
cd YOUR_REPO_NAME

docker build -t php-lamp .

Option A β€” Docker Run

docker run -d \
  -p 7860:7860 \
  -e MYSQL_USER=admin \
  -e MYSQL_PASSWORD=yourpassword \
  -e MYSQL_DATABASE=mydb \
  --name php-lamp \
  php-lamp
# docker-compose.yml
version: '3.8'

services:
  php-lamp:
    build: .
    ports:
      - "7860:7860"
    environment:
      - MYSQL_USER=admin
      - MYSQL_PASSWORD=yourpassword
      - MYSQL_DATABASE=mydb
    restart: unless-stopped
docker compose up -d

Access Your Server

http://localhost:7860/      β†’ Local machine
http://YOUR_VPS_IP:7860/   β†’ Remote VPS

⚠️ On VPS: Open port 7860 in your firewall first:

sudo ufw allow 7860   # Ubuntu / Debian

🌐 Access URLs

Tool Default URL Env Variable
🏠 Website / β€”
πŸ—„οΈ Database UI /sql SQL_PATH
πŸ“ File Manager /files FILES_PATH
πŸ’» Web Terminal /terminal TERMINAL_PATH

Web root directory: /var/www/localhost/htdocs

All tool paths are fully customizable via environment variables β€” set them in Space Settings (or .env) to hide the default URLs from public discovery. For example, setting SQL_PATH=x7k2mdb means phpMyAdmin is only accessible at /x7k2mdb.


πŸ—„οΈ Database Setup

Default Credentials

Username : admin
Password : admin
Database : admin
Host     : 127.0.0.1
Port     : 3306

⚠️ Change the password before going live on a public server!

How to Change Credentials

On Hugging Face: Space β†’ Settings β†’ Variables and Secrets β†’ update the values.

On VPS / Local: Update ENV variables in your docker run command or docker-compose.yml.


πŸ’Ύ Persistent Storage

🚨 This step is REQUIRED β€” the database will not work without it!

MariaDB stores all its data at /data/mysql inside the container. If /data is not mounted as persistent storage, the entire database is wiped every time the Space restarts or rebuilds.

Setting Up Persistent Storage on Hugging Face

  1. Open your Space β†’ click the β€œSettings” tab
  2. Scroll down to the β€œPersistent Storage” section
  3. Configure it as follows:
Field Value
Permission Read & Write
Mount path /data
Storage size Choose as needed (free tier: up to 50GB)
Visibility Private (your data stays secure)

πŸ”’ Always keep your Persistent Storage bucket set to Private. The storage bucket holds your entire database β€” keeping it private ensures that no one else can access or browse your data files. Your Space itself can remain Public (so your website is accessible), while the storage bucket stays Private (so your database is protected). These are two separate settings β€” one does not affect the other.

  1. Click β€œAdd storage” and wait for the Space to restart βœ…

Once mounted, the /data/mysql directory will survive restarts and rebuilds β€” your database tables and data are fully preserved automatically. No extra configuration needed.

The container handles everything internally on first boot:

# First run β€” initializes the database at /data/mysql
mariadb-install-db --datadir=/data/mysql --skip-test-db --user=1000

# Every run β€” starts MariaDB using /data/mysql as datadir
mariadbd --datadir=/data/mysql --bind-address=127.0.0.1

πŸ’‘ You don’t need to run these manually β€” start.sh does it automatically.


βš™οΈ Environment Variables & .env

You can configure your PHP app using a .env file β€” no need to hardcode sensitive values like API keys or database credentials in your code.

How It Works

Place a .env file in your web root:

/var/www/localhost/htdocs/.env

The server automatically loads it at startup, before Apache and MariaDB start. All variables are then available to your PHP app via getenv() or $_ENV.

Example .env File

# App config
APP_NAME=MyApp
APP_ENV=production
APP_DEBUG=false

# Third-party API keys
STRIPE_KEY=sk_live_xxxxxxxxxxxx
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=587
MAIL_USERNAME=your@email.com
MAIL_PASSWORD=yourpassword

Accessing Variables in PHP

$appName  = getenv('APP_NAME');
$stripe   = getenv('STRIPE_KEY');

// Or via $_ENV superglobal
$debug = $_ENV['APP_DEBUG'];

⚠️ Important Rules

πŸ’‘ .env variables are loaded after the MYSQL_USER, MYSQL_PASSWORD, and MYSQL_DATABASE env vars set in Space Settings β€” so they can override them if needed.


πŸ“ File Manager

This setup uses TinyFileManager β€” accessible at /files.

By default, authentication is disabled for easy development.

πŸ” Enable Login (Required for Public Servers!)

  1. Open Web Terminal (/terminal) or File Manager (/files)
  2. Navigate to:
    /usr/share/webapps/filemanager/
    
  3. Open index.php and find:
    $use_auth = false;
    
  4. Change it to:
    $use_auth = true;
    
  5. Save β€” the login screen is now active! βœ…

Default File Manager Credentials

Username : admin
Password : admin@123

πŸ’‘ Change the password via the Settings βš™οΈ icon inside TinyFileManager.


πŸ’» Web Terminal

A custom PHP-powered browser shell is available at /terminal β€” no SSH required.

πŸ“Š System Monitoring

free -h        # RAM usage (Total / Used / Free)
df -h          # Total disk storage
du -sh *       # File sizes in current folder

πŸ“‚ File Navigation

pwd            # Current directory
ls -la         # List files with permissions
cd folder      # Navigate into a folder

πŸ› οΈ Developer Commands

php -v         # Check PHP version
composer -v    # Check Composer version
git --version  # Check Git version
unzip file.zip # Extract a zip archive
nano file.php  # Edit a file in terminal

πŸ”€ Custom Domain via Cloudflare Workers

🎭 Hide your Hugging Face URL β€” Serve your app from your own domain (e.g. yoursite.com) while the actual server stays on Hugging Face behind the scenes.

This method uses a Cloudflare Worker as a reverse proxy. All traffic hits your domain first, gets forwarded to your HF Space, and the response is served back β€” visitors never see the *.hf.space URL.

Prerequisites:


Step 1 β€” Connect Your Domain to Cloudflare

If your domain is not already on Cloudflare:

  1. Log in to dash.cloudflare.com
  2. Click β€œAdd a Site” β†’ enter your domain name
  3. Select the Free plan β†’ click Continue
  4. Cloudflare will scan your existing DNS records β€” review and confirm
  5. Copy the two Cloudflare nameservers shown (e.g. alice.ns.cloudflare.com)
  6. Go to your domain registrar (GoDaddy, Namecheap, etc.) β†’ update the nameservers to the ones Cloudflare gave you
  7. Wait for propagation β€” usually takes 5–30 minutes (up to 48 hours in rare cases)
  8. Once active, Cloudflare will send a confirmation email βœ…

Step 2 β€” Create a Cloudflare Worker

  1. In Cloudflare dashboard β†’ go to Workers & Pages (left sidebar)
  2. Click β€œCreate application”
  3. Click β€œCreate Worker”
  4. Give your worker a name (e.g. my-php-proxy)
  5. Click β€œDeploy” β€” this creates a default Hello World worker

Step 3 β€” Replace the Worker Code

  1. After deploying, click β€œEdit code”
  2. Delete all the existing code in the editor
  3. Paste the following worker script:
export default {
  async fetch(request) {
    try {
      const url = new URL(request.url);

      // πŸ” Replace this with your actual Hugging Face Space URL
      const backendHost = "YOUR_USERNAME-YOUR_SPACE_NAME.hf.space";

      const backendUrl = new URL(request.url);
      backendUrl.hostname = backendHost;
      backendUrl.protocol = "https:";

      const newHeaders = new Headers(request.headers);
      newHeaders.set("X-Forwarded-Host", url.hostname);
      newHeaders.set("X-Forwarded-Proto", "https");

      if (newHeaders.has("origin")) {
        newHeaders.set("origin", `https://${backendHost}`);
      }

      if (newHeaders.has("referer")) {
        try {
          const referer = new URL(newHeaders.get("referer"));
          referer.hostname = backendHost;
          referer.protocol = "https:";
          newHeaders.set("referer", referer.toString());
        } catch {}
      }

      const body = request.method === "GET" || request.method === "HEAD"
        ? undefined
        : await request.arrayBuffer();

      const response = await fetch(new Request(backendUrl.toString(), {
        method: request.method,
        headers: newHeaders,
        body: body,
        redirect: "manual",
      }));

      const headers = new Headers();

      for (const [key, value] of response.headers.entries()) {
        if (key.toLowerCase() === "set-cookie") continue;
        if (key.toLowerCase() === "location") {
          try {
            const loc = new URL(value);
            loc.hostname = url.hostname;
            loc.protocol = url.protocol;
            headers.set("location", loc.toString());
          } catch {
            headers.set("location", value);
          }
          continue;
        }
        headers.set(key, value);
      }

      for (const cookie of response.headers.getAll("set-cookie")) {
        headers.append("set-cookie", cookie
          .replace(/;\s*Domain=[^;]*/gi, "")
          .replace(/;\s*SameSite=[^;]*/gi, "")
          .concat("; SameSite=Lax")
        );
      }

      // Security headers
      headers.delete("x-powered-by");
      headers.delete("server");
      headers.delete("cf-cache-status");
      headers.set("X-Frame-Options", "SAMEORIGIN");
      headers.set("X-Content-Type-Options", "nosniff");
      headers.set("Referrer-Policy", "strict-origin-when-cross-origin");
      headers.set("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
      headers.set("X-XSS-Protection", "1; mode=block");

      return new Response(response.body, {
        status: response.status,
        statusText: response.statusText,
        headers,
      });

    } catch (err) {
      return new Response(`Worker Error: ${err.message}`, { status: 500 });
    }
  },
};
  1. Important: Replace YOUR_USERNAME-YOUR_SPACE_NAME.hf.space on line 6 with your actual HF Space URL.
    For example: shkumaraman-backend.hf.space

  2. Click β€œDeploy” βœ…


Step 4 β€” Attach Your Domain to the Worker

You need to complete both parts below β€” Simple domain mapping AND the Route pattern. Both are required for the worker to handle your domain correctly.


Part A β€” Simple Domain Mapping

  1. In your Worker β†’ go to β€œSettings” tab
  2. Under β€œDomains & Routes” β†’ click β€œAdd”
  3. A dialog appears β€” select β€œCustom Domain”
  4. You will see a domain input field β€” leave it empty
  5. Click β€œAdd domain”

This registers your domain with the Worker directly.


Part B β€” Route Pattern (Advanced URL Matching)

  1. Under β€œDomains & Routes” β†’ click β€œAdd” again
  2. This time select β€œRoute”
  3. The β€œAdvanced URL matching” section appears
  4. In the Route pattern field β€” it will auto-fill with:
    *.yourdomain.com/*
    
  5. Click β€œAdd domain” βœ…

πŸ’‘ The *.yourdomain.com/* pattern covers all subdomains and all paths automatically. Do not change it unless you need a more specific rule.


Step 5 β€” Done! πŸŽ‰

Your custom domain now proxies all traffic to your Hugging Face Space.

https://yourdomain.com/        β†’ Your website
https://yourdomain.com/sql     β†’ phpMyAdmin
https://yourdomain.com/files   β†’ File Manager
https://yourdomain.com/terminal β†’ Web Terminal

The original *.hf.space URL still works too β€” the Worker doesn’t disable it, it just adds your custom domain on top.


πŸ” What the Worker Does (Security Overview)

Feature What it does
Proxy Forwards all requests to your HF Space and returns the response
URL masking Rewrites Location headers on redirects so visitors stay on your domain
Cookie handling Strips Domain and SameSite attributes so cookies work cross-domain
Security headers Adds HSTS, X-Frame-Options, X-Content-Type-Options, and more
Header cleanup Removes x-powered-by and server headers to reduce fingerprinting

πŸ› οΈ Troubleshooting

Domain not routing to the worker?

Getting a Cloudflare error page?

Cookies or sessions not working?

Worker only needed on one subdomain?


πŸ’‘ Pro Tips


🀝 Contributing

Contributions, issues, and feature requests are welcome!

  1. Fork the repository
  2. Create your feature branch: git checkout -b feature/amazing-feature
  3. Commit your changes: git commit -m 'Add amazing feature'
  4. Push to the branch: git push origin feature/amazing-feature
  5. Open a Pull Request πŸŽ‰

**Made with ❀️ for developers who love simplicity** ⭐ **If this helped you, please give it a Star!** ⭐
Logo

Please Wait

WhatsApp Channel Join for updates
Join
TalkRush App Add to home screen