Nginx and PHP

d.rymcg.tech ships with a traditional PHP webstack, including Nginx proxy, with PostgreSQL database and Redis session store. These extra features can all be turned off, turning Nginx into a simple static file server instead.

Configure Nginx

Run this on your Raspberry Pi
pi make nginx config

Make sure you choose a unique domain for this service (www is also the default for thttpd, so if you installed that too, just make sure this is unique:)

(stdout)
NGINX_TRAEFIK_HOST: Enter the nginx domain name (eg. www.example.com)

: nginx.pi.example.com

Choose the optional features you want to enable: postgres, php-fpm:

(stdout)
? Choose the docker-compose profiles to enable:
  [x] nginx - Base Nginx config (don't unselect this, it is required)
  [x] postgres - PostgreSQL database
> [x] php-fpm - PHP script support + Redis session store

Choose one of the default PHP apps to install:

(stdout)
? Choose the index PHP file to install
  default.php
> phpinfo.php
  https://github.com/adminerevo/adminerevo/releases/download/v4.8.3/adminer-4.8.3.php

Turn on one of the sentry authorization options to protect your instance:

(stdout)
? Do you want to enable sentry authorization in front of this app (effectively making the entire site private)?
  No
  Yes, with HTTP Basic Authentication
> Yes, with Oauth2
  Yes, with Mutual TLS (mTLS)

? Which authorization group do you want to permit access to this app?
> admin
Tip

This will allow only the admin authorization group to access this instance.

Remember, you can create extra authorization groups in the Traefik config, that way you can have separate user access per instance.

Install Nginx

Run this on the Raspberry Pi
pi make nginx install wait

Add a new route on the sentry (droplet)

You need to create two routes: one for homepage, and one for the webhooks:

Run this on the Raspberry Pi
sentry make traefik config
(stdout)
? Traefik:
> Config
  Install (make install)
  Admin
  Exit (ESC)

? Traefik Configuration:
^ Entrypoints (including dashboard)
  TLS certificates and authorities
  Middleware (including sentry auth)
> Advanced Routing (Layer 7 / Layer 4 / WireGuard)
  Error page template
  Logging level
  Access logs

? Traefik routes
> Configure layer 7 TLS proxy
  Configure layer 4 TCP/UDP proxy
  Configure wireguard VPN

? Layer 7 TLS Proxy:
  List layer 7 ingress routes
> Add new layer 7 ingress route
  Remove layer 7 ingress routes
  Disable layer 7 TLS Proxy

Enter the public domain (SNI) for the route:

: nginx.pi.example.com

Enter the destination IP address to forward to:

: 10.13.16.2

Enter the destination TCP port to forward to:

: 443

> Do you want to enable Proxy Protocol for this route? Yes

## Layer 7 TLS Proxy is ENABLED.
## Configured Layer 7 Routes:
Entrypoint            Destination_address  Destination_port  Proxy_protocol
----------            -------------------  ----------------  --------------
nginx.pi.example.com  10.13.16.2           443               2

Press ESC three times to go back to the main menu, and re-install Traefik:

(stdout)
? Traefik:
  Config
> Install (make install)
  Admin
  Exit (ESC)

After installation, press ESC to quit the config tool.

Open Nginx webpage

Open https://nginx.pi.example.com in your web browser.

Upload site files with SFTP

Manage the Nginx volume (nginx_files) with SFTP (the same way as with thttpd), and you can use this as a simple way to publish your websites.

Run this on the Raspberry Pi
pi make sftp config
(stdout)
SFTP_PORT: Enter the public SSH port (eg. 2223)

: 2223

SFTP_USERS: Enter the user:uid list (eg. ryan:1000,gary:1001)

: www:54321

SFTP_VOLUMES: Enter the volume:user:mount list (can be blank)

: nginx_files:www:nginx
Run this on the Raspberry Pi
pi make sftp install

Authorize your SSH key:

Run this on the Raspberry Pi
pi make sftp ssh-authorize-key
  • Enter the username nginx
  • Enter the SSH public key (copy this from your local ~/.ssh/id_ed25519.pub)

Create counters demo app

Let’s create a demo PHP script that shows how to connect to the PostgreSQL database and make a web page hit counter:

Create a new project directory on your personal workstation:

[bash]: Run this on your workstation:
mkdir -p ~/php-demo
[bash]: Run this on your workstation:
cat <<'EOF' > ~/php-demo/counter.php
<?php
// Load environment variables using the standard PostgreSQL environment variable names
$host = getenv('PGHOST') ?: 'localhost'; // Default to localhost if PGHOST is not set
$dbname = getenv('PGDATABASE') ?: 'your_default_database';
$user = getenv('PGUSER') ?: 'your_default_user';
$password = getenv('PGPASSWORD') ?: 'your_default_password';
$port = getenv('PGPORT') ?: '5432'; // Default to port 5432 if PGPORT is not set

// Check if all the necessary environment variables are set
if (!$host || !$dbname || !$user || !$password || !$port) {
    die('Missing required PostgreSQL environment variables.');
}

// Establish a connection to the PostgreSQL database
try {
    $dsn = "pgsql:host=$host;port=$port;dbname=$dbname";
    $pdo = new PDO($dsn, $user, $password, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
} catch (PDOException $e) {
    die('Connection failed: ' . $e->getMessage());
}

// Function to check if the table exists
function checkTableExists($pdo, $tableName) {
    $query = $pdo->prepare("SELECT to_regclass(:table_name) AS exists");
    $query->execute(['table_name' => $tableName]);
    $result = $query->fetch(PDO::FETCH_ASSOC);
    return $result['exists'] !== null;
}

// Create the table if it does not exist
if (!checkTableExists($pdo, 'page_counters')) {
    $createTableQuery = "
    CREATE TABLE page_counters (
        id SERIAL PRIMARY KEY,
        counter_name VARCHAR(255) UNIQUE NOT NULL,
        count BIGINT DEFAULT 0
    );
    ";
    $pdo->exec($createTableQuery);
}

// Determine the counter name from the query string, defaulting to 'default'
$counterName = isset($_GET['counter']) ? $_GET['counter'] : 'default';

// Check if the counter exists in the database
$stmt = $pdo->prepare("SELECT count FROM page_counters WHERE counter_name = :counter_name");
$stmt->execute(['counter_name' => $counterName]);
$counter = $stmt->fetch(PDO::FETCH_ASSOC);

if ($counter) {
    // If the counter exists, increment it
    $newCount = $counter['count'] + 1;
    $updateStmt = $pdo->prepare("UPDATE page_counters SET count = :count WHERE counter_name = :counter_name");
    $updateStmt->execute(['count' => $newCount, 'counter_name' => $counterName]);
} else {
    // If the counter does not exist, create it with a value of 1
    $newCount = 1;
    $insertStmt = $pdo->prepare("INSERT INTO page_counters (counter_name, count) VALUES (:counter_name, :count)");
    $insertStmt->execute(['counter_name' => $counterName, 'count' => $newCount]);
}

// Output the current counter value
echo "Counter '$counterName': $newCount";
EOF
[bash]: Run this on your workstation:
scp ~/php-demo/counter.php sftp.pi.example.com:nginx/public/
Tip

Instead of scp, you can follow the same instructions as for Thttpd, and setup Rclone with an easy to use sync-web alias for synchronizing your web files.

Visit counter page

Try these URLs in your browser (replacing your root domain):

  • https://nginx.pi.example.com/counter.php
  • https://nginx.pi.example.com/counter.php?counter=two

Refresh each one multiple times and watch the counter grow.