Deploying Ring Web Applications to Shared Hosting

Chapter Author: Youssef Saeed

While modern application deployment often involves containers, many hosting environments—especially traditional shared hosting panels like cPanel and Plesk—do not allow running persistent background processes. For these platforms, the classic CGI (Common Gateway Interface) model remains the perfect and most compatible solution.

This tutorial guides you through deploying Ring applications as CGI scripts. We will use a powerful, secure CGI wrapper script that makes the process robust and reliable across different hosting environments.

1. Introduction: The CGI Model

CGI is a standard protocol that allows a web server (like Apache or Nginx) to execute external scripts to generate web pages dynamically.

Docker / Modern Server Model

Classic CGI Model

Your Ring app is a long-running server using httplib.

Your Ring app is a simple script that runs and exits on each request.

The web server acts as a Reverse Proxy, forwarding traffic.

The web server acts as an Executor, running your script directly.

Requires root or sudo access on a VM to run Docker.

Works on virtually any shared hosting plan with minimal permissions.

Deployment is typically done via docker compose up.

Deployment is done by uploading files (e.g., via FTP/SFTP).

The CGI model is incredibly portable and has been a workhorse of the web for decades, making it ideal for environments with limited control.

2. Prerequisites

  • Access to a web hosting environment (either a shared hosting panel or a cloud VM with sudo access).

  • A way to upload files (e.g., a File Manager in your control panel, or an SFTP client like FileZilla).

  • A basic understanding of Ring syntax.

  • Crucially, the Ring language itself must be uploaded to your hosting environment.

3. Creating a CGI-Compatible Ring Script

A CGI script is simpler than a full server application. It does not use httplib. Instead, it follows a simple contract:

  1. Print a Content-Type header (e.g., Content-Type: text/html).

  2. Print a single blank line.

  3. Print the HTML body content.

  4. Exit.

Create a file named hello.ring with the following content.

# A minimal CGI script
See "Content-Type: text/html" + nl + nl

See "<html>"
See "<head><title>CGI Test</title></head>"
See "<body>"
See "<h1>Hello from a Ring CGI Script!</h1>"
See "<p>This page was generated by Ring running as a CGI application.</p>"
See "</body>"
See "</html>"

4. The Universal Ring CGI Wrapper

To make our Ring scripts work reliably and securely, we will use a “wrapper.” This is a Bash script that the web server executes. Its job is to correctly prepare the environment and then run our .ring file.

This wrapper cleverly handles different hosting configurations, sets up necessary library paths, and includes crucial security checks. Create a file named ring.cgi with the content below.

#!/bin/bash

# ==============================================================================
# Universal Ring CGI Wrapper
#
# A robust CGI front controller for executing .ring files on a web server.
#
# How it works:
#   1. The web server (via .htaccess) calls this script for any .ring file request.
#   2. The script determines the correct Ring installation path and web root.
#   3. It sets the LD_LIBRARY_PATH so Ring's shared libraries can be found.
#   4. It performs security checks to prevent path traversal attacks.
#   5. It executes the requested .ring script using the Ring compiler in CGI mode.
# ==============================================================================

# --- Configuration ----------------------------------------------------

# If the HOME environment variable is not set (common in some CGI environments),
# this script attempts to deduce it from the current working directory (PWD).
if [ -z "$HOME" ]; then
  # Guess home directory for various hosting panels.
  # Plesk: /var/www/vhosts/domain.com/httpdocs/cgi-bin
  #     or /home/domain.com/httpdocs/cgi-bin
  # cPanel/DirectAdmin: /home/username/public_html/cgi-bin
  # KeyHelp: /home/users/username/www/cgi-bin
  # ispManager: /var/www/username/data/www/domain/cgi-bin
  if [[ "$PWD" == /var/www/vhosts/* ]]; then
    HOME_DIR_GUESS="${PWD%/httpdocs*}"
  elif [[ "$PWD" == /home/users/* ]]; then
    HOME_DIR_GUESS="${PWD%/www*}"
  elif [[ "$PWD" == /home/*/public_html* ]]; then
    HOME_DIR_GUESS="${PWD%/public_html*}"
  elif [[ "$PWD" == /home/*/httpdocs* ]]; then
    HOME_DIR_GUESS="${PWD%/httpdocs*}"
  elif [[ "$PWD" == /var/www/*/data/* ]]; then
    HOME_DIR_GUESS="${PWD%%/data/*}/data"
  else
    # Fallback to the current directory if no pattern matches.
    HOME_DIR_GUESS="$PWD"
  fi
  RING_DIR="$HOME_DIR_GUESS/ring"
else
  RING_DIR="$HOME/ring"
fi

# Full path to the Ring executable.
RING_EXECUTABLE="$RING_DIR/bin/ring"

# WEB_ROOT: Absolute path to your site's document root.
# The script will try to guess this by removing /cgi-bin from the end of the path.
# You can override this by setting a RING_WEB_ROOT environment variable.
WEB_ROOT_GUESS="${PWD%/cgi-bin*}"
export RING_WEB_ROOT="${RING_WEB_ROOT:-$WEB_ROOT_GUESS}"

# Ensure the dynamic linker can find Ring's shared libraries.
export LD_LIBRARY_PATH="${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}$RING_DIR/lib"


# --- Main Script Logic ------------------------------------------------

# The web server passes the full file path of the requested .ring script
# in the PATH_TRANSLATED environment variable.
TARGET_RING_SCRIPT="$PATH_TRANSLATED"

# Check 1: Ensure the target script exists.
if [ ! -f "$TARGET_RING_SCRIPT" ]; then
    echo "Content-Type: text/html"
    echo ""
    echo "<h1>404 Not Found</h1>"
    echo "<p>The requested Ring script could not be found.</p>"
    exit 0
fi

# Security Check: Prevent path traversal attacks.
# Ensure the canonical path of the target script is within the web root.
REAL_TARGET_PATH=$(realpath -s "$TARGET_RING_SCRIPT")

if [[ "$REAL_TARGET_PATH" != "$RING_WEB_ROOT"* ]]; then
    echo "Content-Type: text/html"
    echo ""
    echo "<h1>403 Forbidden</h1>"
    echo "<p>Access to the requested resource is not allowed.</p>"
    exit 0
fi

# Check 2: Ensure the Ring executable is found and has execute permissions.
if [ ! -x "$RING_EXECUTABLE" ]; then
    echo "Content-Type: text/html"
    echo ""
    echo "<h1>500 Server Configuration Error</h1>"
    echo "<p>The Ring Compiler/VM could not be found or is not executable. Check that the 'ring' folder was uploaded to your home directory.</p>"
    exit 0
fi

# Change to the script's directory so file operations are relative to it.
pushd "$(dirname "$TARGET_RING_SCRIPT")" > /dev/null

# Execute the Ring script in CGI mode.
# The Ring script is responsible for printing all headers and content.
"$RING_EXECUTABLE" -cgi "$TARGET_RING_SCRIPT"

# Return to the original directory.
popd > /dev/null

exit 0

5. Deployment Scenarios

Choose the path that matches your hosting environment.

Path A: Shared Hosting with .htaccess (Apache/LiteSpeed)

This is the most common scenario. It relies on a .htaccess file to tell the web server how to handle .ring files.

Step 1: Upload the Ring Language

  1. Download the Ring release for Linux from the official website.

  2. On your local machine, extract the ring folder from the archive.

  3. Using an SFTP client or your hosting panel’s File Manager, upload the entire ring folder to your home directory (e.g., /home/youruser). The final structure must be /home/youruser/ring.

Step 2: Upload and Configure the CGI Wrapper

  1. Using the File Manager, navigate to your web root (usually public_html, httpdocs, or www).

  2. If it doesn’t exist, create a folder named cgi-bin.

  3. Upload the ring.cgi script you created earlier into this cgi-bin folder.

  4. Set its permissions to 755 (rwx r-x r-x). This is crucial to make it executable. You can typically do this by right-clicking the file in the File Manager and choosing “Change Permissions.”

Step 3: Create the .htaccess File

  1. In your web root (public_html, httpdocs, etc.), create a new file named .htaccess.

  2. Add the following content. This tells the web server to use our wrapper script for any file ending in .ring.

    # Allow CGI scripts to be executed from this directory.
    Options +ExecCGI
    
    # Define a custom handler named 'ring-script' for all .ring files.
    AddHandler ring-script .ring
    
    # Specify that our wrapper script should execute files for the 'ring-script' handler.
    # The path should be relative to the web root.
    Action ring-script /cgi-bin/ring.cgi
    

Step 4: Upload and Test Your Ring Application

  1. Upload your hello.ring file to your web root.

  2. In your browser, navigate to http://your-domain.com/hello.ring.

If everything is configured correctly, you should see the “Hello from a Ring CGI Script!” message.

Path B: Cloud VM with Nginx & FastCGI

If you have sudo access on a VM and use Nginx, fcgiwrap is the standard, high-performance way to run CGI scripts.

Step 1: Install Dependencies

SSH into your VM and install Nginx and the FastCGI wrapper.

sudo apt update
sudo apt install nginx fcgiwrap

Step 2: Enable and Start Services

Ensure both services start on boot and are running now.

sudo systemctl enable --now nginx
sudo systemctl enable --now fcgiwrap

Step 3: Install Ring in a System Location

  1. Upload or move the ring folder to /opt/. The final location must be /opt/ring.

    # If already uploaded to your home directory:
    sudo mv ~/ring /opt/
    
  2. Give the web server user (www-data) ownership and permissions.

    sudo chown -R www-data:www-data /opt/ring
    sudo chmod -R 755 /opt/ring
    

Step 4: Make the Ring Executable System-Wide

This allows scripts to find the ring command without a full path.

cd /opt/ring/bin
sudo bash install.sh

Step 5: Create a Directly Executable Ring Script

For this method, your script must have a “shebang” line pointing to the system-wide ring executable. Create or edit hello.ring to look like this:

#!/usr/bin/ring -cgi

# This script is now directly executable.
See "Content-Type: text/html" + nl + nl

See "<html>"
See "<body>"
See "<h1>Hello from Nginx and FastCGI!</h1>"
See "</body>"
See "</html>"

Step 6: Upload Script and Set Permissions

  1. Upload hello.ring to your Nginx web root (typically /var/www/html).

  2. Make the script itself executable.

    sudo chmod 755 /var/www/html/hello.ring
    

Step 7: Configure Nginx

Edit your Nginx site configuration (e.g., /etc/nginx/sites-available/default) and add a location block to handle .ring files.

server {
    listen 80;
    server_name your-domain.com;
    root /var/www/html;
    index index.html;

    # ... other configurations ...

    # Pass .ring scripts to the fcgiwrap socket for execution.
    location ~ \.ring$ {
        include fastcgi_params;
        fastcgi_pass unix:/var/run/fcgiwrap.socket;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
}

Step 8: Restart and Test

  1. Reload Nginx to apply the new configuration.

    sudo systemctl reload nginx
    
  2. Navigate to http://your-domain.com/hello.ring.

This method is more involved but is the standard, secure way to integrate CGI with Nginx.

6. Platform-Specific Guides for Shared Hosting

For Path A, here are specific tips for popular control panels.

A Crucial Note on Host-Level CGI Support

Before you begin, understand that the .htaccess method depends on your hosting provider allowing CGI execution. Our .htaccess file uses Options +ExecCGI, but some hosts disable this for security.

Troubleshooting Tip: If you follow the steps for Path A and see a “500 Internal Server Error,” the most common cause is a server-level restriction.

Your first step should be to contact your hosting provider’s support team and ask them this specific question:

“Is CGI script execution enabled for my account, and am I allowed to use the Options +ExecCGI directive in my .htaccess file?”

Confirming this first can save you hours of debugging.

cPanel

  • Tested & Confirmed: The .htaccess method works flawlessly on cPanel, which typically runs on an Apache or LiteSpeed web server.

  • File Uploads: Use the File Manager tool. Your web root, the folder where website files are publicly accessible, is public_html. This folder is located inside your home directory, which has a full path like /home/username/public_html/.

  • Permissions: In File Manager, right-click on the ring.cgi file and select Change Permissions. Enter 755 and save to make the script executable. By default, files often have 0644 permissions and folders have 0755.

  • Creating .htaccess : In File Manager, you can create a new file by clicking the + File button. To view existing .htaccess files, which are hidden by default, go to the Settings menu in the top right and check the box for Show Hidden Files (dotfiles).

  • CGI Status: CGI is generally enabled on cPanel servers. The server looks for a cgi-sys/defaultwebpage.cgi when a domain does not have a configured VirtualHost or is pointed to the wrong IP, indicating CGI is active. Including the Options +ExecCGI directive in your .htaccess file can help ensure that CGI scripts are executed in your specific directory.

Plesk

  • Tested & Confirmed: The .htaccess method is effective on Plesk servers running Apache. If the server uses Nginx as a proxy, you must ensure Apache is also enabled and processes requests for .htaccess to work.

  • File Uploads: Use the Files or File Manager tab. Your web root is typically the httpdocs directory.

  • Permissions: In the Files tab, click the three-dot menu next to the ring.cgi file and choose Change Permissions. To make the script executable, ensure the Execute permission is checked for the “Owner” and “Group” users.

  • .htaccess Support: For .htaccess files to work, go to your domain’s Apache & Nginx Settings and ensure that Apache is enabled and that requests are not being handled exclusively by Nginx.

  • CGI Status: To enable CGI script execution, go to the domain’s Hosting Settings and ensure that CGI support is enabled. You may also need to configure the handler in the PHP Settings page by adding an AddHandler directive for .cgi files in the “Additional Apache directives” section.

DirectAdmin

  • Tested & Confirmed: The .htaccess method works as expected, often on servers running LiteSpeed or Apache.

  • File Uploads: Use the System Info & Files -> File Manager. Your web root directory is public_html.

  • Permissions: In the File Manager, hover over the ring.cgi file and select Set Permissions (this may also be found by right-clicking). Set the permission code to 755 to make it executable. By default, folders are often 755 and files are 644.

KeyHelp

  • Tested & Confirmed: The .htaccess method works as described.

  • File Uploads: Use the Files -> File Manager. Your web root is typically /www inside your user’s home directory (/home/users/username/www).

  • Permissions: Within the File Manager, you can change a file’s permissions. Click on the file and adjust the permissions as needed (e.g., from 0644 to 0755 to make a script executable).

  • CGI Status: CGI is not enabled by default for users. The server administrator must first enable the “Perl/CGI” permission for the specific user account. Once enabled, .htaccess directives can be used to manage CGI script execution. The ring.cgi wrapper’s logic should function correctly within KeyHelp’s structure, provided the necessary permissions are set.

ispManager

  • Tested & Confirmed: The .htaccess method works as expected.

  • File Uploads: Use the File Manager. Your web root is typically located at /var/www/username/data/www/domain, where username is your account name and domain is your website’s domain name.

  • Permissions: In the File Manager, select the ring.cgi file, click Edit, and then choose Attributes. Set the permissions to 755 to make it executable. By default, files are often set to 644, which does not allow execution.

  • CGI Status: CGI support is usually enabled by default in ispManager. However, if you encounter issues, check the server settings or contact your hosting provider to ensure that CGI execution is permitted for your account. The ring.cgi wrapper should work correctly within ispManager’s environment, provided the necessary permissions are set.

7. Security Considerations

  • Error Logging: For a production site, prevent detailed error messages from being shown to users. Modify the execution line in ring.cgi to redirect errors to a log file:

    # In ring.cgi, change the execution line to this:
    "$RING_EXECUTABLE" -cgi "$TARGET_RING_SCRIPT" 2>>/path/to/your/logs/ring_errors.log
    

    Replace the path with a directory that is not inside your public web root.

  • File Permissions: Never set permissions to 777. This allows anyone to modify your scripts. The 755 permission is correct for executable scripts.

  • Input Validation: Always sanitize and validate any user input (like query strings or form data) within your Ring scripts to prevent security vulnerabilities like SQL injection or Cross-Site Scripting (XSS).

8. Conclusion

You now know how to deploy Ring applications to a wide range of hosting environments using the highly compatible CGI model.

  • Path A (Shared Hosting) is perfect for getting started quickly on affordable hosting plans where you have limited server control.

  • Path B (Cloud VM) offers higher performance and a more standard setup for users who manage their own server with Nginx.

By mastering both server and CGI deployment methods, you gain the flexibility to run your Ring applications almost anywhere.