ProjectSend is a self-hosted file sharing application for users or clients that avoids handing control over to a third-party service. It runs on standard web infrastructure, keeps data local, and focuses on managing uploads, access, and downloads in a straightforward way.
The accompanying install script exists for the same reason. Instead of locking the system to a specific PHP release or assuming yesterday’s defaults still apply, it selects a supported PHP version at install time based on current ProjectSend requirements and available packages. The script installs and configures Apache, MariaDB, PHP, firewall rules, and SELinux settings in a deliberate order, with no hidden steps. Credentials are generated securely, services are enabled cleanly, and the script can be re-run without causing collateral damage.
Requirements / Tested Environment
This setup has been tested using the following components:
Operating System: Rocky Linux 8 and 9 (RHEL-compatible)
Web Server: Apache HTTPD with mod_ssl
Database: MariaDB (MySQL-compatible)
PHP: PHP 8.1 or newer (via Remi repository)
SELinux: Enforcing
Firewall: firewalld enabled
The install script dynamically selects a supported PHP version based on current ProjectSend documentation and available packages, avoiding hard-coded dependencies while remaining compatible with RHEL-based systems.
#!/bin/bash
set -euo pipefail
### CONFIG ###
HOSTNAME="inf-files"
WEBROOT="/var/www/html"
DB_NAME="psdb"
DB_USER="psuser"
DB_PASS_FILE="/root/.ps_db_pass"
TIMEZONE="UTC"
### REQUIRE ROOT ###
if [[ $EUID -ne 0 ]]; then
echo "Run as root"
exit 1
fi
echo "Starting ProjectSend installation..."
### HOSTNAME & TIMEZONE ###
hostnamectl set-hostname "$HOSTNAME"
timedatectl set-timezone "$TIMEZONE"
### REPOSITORIES ###
dnf -y install epel-release
dnf -y install https://rpms.remirepo.net/enterprise/remi-release-$(rpm -E %rhel).rpm
### SELECT SUPPORTED PHP VERSION ###
PHP_CANDIDATES=("8.2" "8.1" "8.0" "7.4")
PHP_VERSION=""
for v in "${PHP_CANDIDATES[@]}"; do
if dnf module list php 2>/dev/null | grep -q "remi-${v}"; then
PHP_VERSION="$v"
break
fi
done
if [[ -z "$PHP_VERSION" ]]; then
echo "No supported PHP module found."
exit 1
fi
echo "Using PHP ${PHP_VERSION}"
dnf -y module reset php
dnf -y module enable php:remi-${PHP_VERSION}
### PACKAGES ###
dnf -y install \
httpd \
mariadb \
mariadb-server \
mod_ssl \
unzip \
curl \
openssl \
php \
php-cli \
php-common \
php-gd \
php-json \
php-ldap \
php-mysqlnd \
php-zip \
php-snmp \
php-mbstring \
php-xml
### SERVICES ###
systemctl enable --now httpd mariadb firewalld
### FIREWALL ###
firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=https
firewall-cmd --reload
### DATABASE PASSWORD ###
if [[ ! -f "$DB_PASS_FILE" ]]; then
DB_PASS=$(openssl rand -base64 24)
echo "$DB_PASS" > "$DB_PASS_FILE"
chmod 600 "$DB_PASS_FILE"
else
DB_PASS=$(cat "$DB_PASS_FILE")
fi
### DATABASE SETUP ###
mysql <<EOF
CREATE DATABASE IF NOT EXISTS ${DB_NAME};
CREATE USER IF NOT EXISTS '${DB_USER}'@'localhost' IDENTIFIED BY '${DB_PASS}';
GRANT ALL PRIVILEGES ON ${DB_NAME}.* TO '${DB_USER}'@'localhost';
FLUSH PRIVILEGES;
EOF
### FETCH LATEST PROJECTSEND ###
LATEST_TAG=$(curl -s https://api.github.com/repos/projectsend/projectsend/releases/latest \
| grep -oP '"tag_name": "\K[^"]+')
if [[ -z "$LATEST_TAG" ]]; then
echo "Failed to retrieve latest ProjectSend version."
exit 1
fi
ARCHIVE="/tmp/projectsend-${LATEST_TAG}.zip"
curl -L -o "$ARCHIVE" \
"https://github.com/projectsend/projectsend/releases/download/${LATEST_TAG}/projectsend-${LATEST_TAG}.zip"
unzip -o "$ARCHIVE" -d "$WEBROOT"
### CONFIG FILE ###
CONFIG="${WEBROOT}/includes/sys.config.php"
SAMPLE="${WEBROOT}/includes/sys.config.sample.php"
if [[ ! -f "$CONFIG" ]]; then
cp "$SAMPLE" "$CONFIG"
sed -i \
-e "s/define('DB_NAME'.*/define('DB_NAME', '${DB_NAME}');/" \
-e "s/define('DB_USER'.*/define('DB_USER', '${DB_USER}');/" \
-e "s/define('DB_PASSWORD'.*/define('DB_PASSWORD', '${DB_PASS}');/" \
"$CONFIG"
fi
### PERMISSIONS ###
chown -R apache:apache "$WEBROOT"
find "$WEBROOT" -type d -exec chmod 755 {} \;
find "$WEBROOT" -type f -exec chmod 644 {} \;
### SELINUX ###
setsebool -P httpd_can_network_connect on
setsebool -P httpd_unified on
restorecon -FR "$WEBROOT"
### VERIFY PHP ###
php -v | grep -E "PHP (7\.4|8\.0|8\.1|8\.2)" >/dev/null || {
echo "Unsupported PHP version detected."
exit 1
}
### COMPLETE ###
echo
echo "Installation complete."
echo "URL: http://$(hostname -f)/"
echo "Database: ${DB_NAME}"
echo "DB User: ${DB_USER}"
echo "DB Password stored at: ${DB_PASS_FILE}"
echo "PHP Version: ${PHP_VERSION}"
echo