dotlinux blog

Installing FcgiWrap and Enabling Perl, Ruby, and Bash Dynamic Languages on Gentoo LEMP

The classic LEMP stack (Linux, Nginx, MySQL/MariaDB, PHP) is a powerhouse for serving dynamic web content, primarily through PHP-FPM. However, the web is a diverse ecosystem, and sometimes your application logic is best written in other languages like Perl, Ruby, or even simple Bash scripts. While Nginx itself doesn't have a built-in module to handle these languages like Apache's mod_* modules, we can bridge this gap using a powerful tool called FcgiWrap.

FcgiWrap acts as a gateway. It implements the FastCGI protocol, allowing Nginx to communicate with it. Then, it takes the web request and executes the corresponding script (e.g., a .pl, .rb, or .sh file) using the appropriate interpreter (Perl, Ruby, Bash). This provides a clean, standardized way to run CGI-like scripts with the performance benefits of the FastCGI protocol.

In this comprehensive guide, we will walk through the process of setting up FcgiWrap on a Gentoo Linux system and configuring the Nginx web server to dynamically execute Perl, Ruby, and Bash scripts. We assume you have a working Gentoo system with a basic LEMP stack already installed and configured.

2026-05

Table of Contents#

  1. Understanding the Components
  2. Installing FcgiWrap on Gentoo
  3. Configuring the Service
  4. Configuring Nginx
  5. Testing the Setup with Scripts
  6. Security Considerations
  7. Troubleshooting Common Issues
  8. Conclusion
  9. References

1. Understanding the Components#

Before we dive in, let's clarify the workflow:

  1. A user's browser requests a resource like http://yourdomain.com/test.pl.
  2. Nginx receives the request.
  3. Based on its configuration, Nginx recognizes the .pl extension and forwards the request to the FcgiWrap process via a FastCGI socket.
  4. FcgiWrap receives the request, starts a new process for the Perl interpreter, and executes the test.pl script.
  5. The script's output (HTML, text, etc.) is sent back through FcgiWrap to Nginx.
  6. Nginx finally delivers the response to the user's browser.

This process is similar for Ruby (.rb) and Bash (.sh/.cgi) scripts.

2. Installing FcgiWrap on Gentoo#

Gentoo's package manager, Portage, makes installation straightforward. We will also install the necessary interpreters.

  1. Update your system: It's always good practice to start with an updated system.

    sudo emerge --sync
    sudo emerge -avuDN @world
  2. Install the required packages: We need fcgiwrap and the language interpreters. If you already have some of these, Portage will skip them.

    sudo emerge -av www-servers/fcgiwrap dev-lang/perl dev-lang/ruby app-shells/bash
    • www-servers/fcgiwrap: The main FcgiWrap package.
    • dev-lang/perl: The Perl interpreter.
    • dev-lang/ruby: The Ruby interpreter.
    • app-shells/bash: The Bash shell (almost certainly already installed).

    Accept the proposed list of packages to install.

3. Configuring the Service#

FcgiWrap needs to run as a service so it's always available for Nginx. The configuration differs slightly depending on your init system.

3.1. OpenRC (Traditional Init)#

  1. Create the service configuration directory and file:

    sudo mkdir -p /etc/init.d
    sudo nano /etc/init.d/fcgiwrap
  2. Add the following content to the file. This script defines how to start, stop, and manage the fcgiwrap daemon. We'll configure it to use a Unix socket for communication.

    #!/sbin/openrc-run
     
    description="fcgiwrap - simple FastCGI wrapper for CGI scripts"
     
    command="/usr/sbin/fcgiwrap"
    command_args="-c 2 -s unix:/var/run/fcgiwrap/fcgiwrap.sock"
    command_user="nginx:nginx"
    command_background=true
     
    pidfile="/var/run/${RC_SVCNAME}.pid"
     
    depend() {
        need net nginx
        use mysql
    }
     
    start_pre() {
        checkpath --directory --owner nginx:nginx --mode 0755 /var/run/fcgiwrap
    }
    • command_args: -c 2 spawns 2 processes to handle requests. You can adjust this number based on your expected load. -s unix:/var/run/fcgiwrap/fcgiwrap.sock defines the socket file location.
    • command_user: It's crucial to run the service as the same user and group as your Nginx worker processes (commonly nginx:nginx). Check your nginx.conf file for the user directive.
    • start_pre: Ensures the runtime directory exists with the correct permissions before starting the service.
  3. Make the script executable and add it to the default runlevel:

    sudo chmod +x /etc/init.d/fcgiwrap
    sudo rc-update add fcgiwrap default
  4. Start the service:

    sudo /etc/init.d/fcgiwrap start

    You can verify it's running with sudo rc-status or by checking if the socket file exists: ls -l /var/run/fcgiwrap/fcgiwrap.sock.

3.2. systemd#

  1. Create a systemd service file:

    sudo nano /etc/systemd/system/fcgiwrap.service
  2. Add the following content:

    [Unit]
    Description=fcgiwrap - simple FastCGI wrapper for CGI scripts
    After=nginx.service
     
    [Service]
    Type=forking
    ExecStart=/usr/sbin/fcgiwrap -c 2 -s unix:/var/run/fcgiwrap/fcgiwrap.sock
    User=nginx
    Group=nginx
    RuntimeDirectory=fcgiwrap
    PIDFile=/var/run/fcgiwrap/fcgiwrap.pid
     
    [Install]
    WantedBy=multi-user.target

    The RuntimeDirectory directive will automatically create /var/run/fcgiwrap with the correct permissions.

  3. Reload systemd, enable, and start the service:

    sudo systemctl daemon-reload
    sudo systemctl enable fcgiwrap.service
    sudo systemctl start fcgiwrap.service

    Verify with sudo systemctl status fcgiwrap.service.

4. Configuring Nginx#

Now we need to tell Nginx to pass requests for specific file extensions to the FcgiWrap socket.

4.1. Creating a FastCGI Parameter File#

It's efficient to create a common file for FastCGI parameters that can be included in multiple server blocks.

  1. Create or edit the parameter file (a common location is /etc/nginx/fastcgi_params or a new one like /etc/nginx/fcgiwrap_params).

    sudo nano /etc/nginx/fcgiwrap_params
  2. Add the standard FastCGI parameters:

    fastcgi_param  QUERY_STRING       $query_string;
    fastcgi_param  REQUEST_METHOD     $request_method;
    fastcgi_param  CONTENT_TYPE       $content_type;
    fastcgi_param  CONTENT_LENGTH     $content_length;
     
    fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;
    fastcgi_param  REQUEST_URI        $request_uri;
    fastcgi_param  DOCUMENT_URI       $document_uri;
    fastcgi_param  DOCUMENT_ROOT      $document_root;
    fastcgi_param  SERVER_PROTOCOL    $server_protocol;
    fastcgi_param  REQUEST_SCHEME     $scheme;
    fastcgi_param  HTTPS              $https if_not_empty;
     
    fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
    fastcgi_param  SERVER_SOFTWARE    nginx/$nginx_version;
     
    fastcgi_param  REMOTE_ADDR        $remote_addr;
    fastcgi_param  REMOTE_PORT        $remote_port;
    fastcgi_param  SERVER_ADDR        $server_addr;
    fastcgi_param  SERVER_PORT        $server_port;
    fastcgi_param  SERVER_NAME        $server_name;
     
    # PHP only, required if PHP was built with --enable-force-cgi-redirect
    # fastcgi_param  REDIRECT_STATUS    200;

4.2. Setting Up a Virtual Host#

Within your Nginx server block configuration (e.g., /etc/nginx/sites-available/example.com), you need to add a location block for each language you want to support.

Here is an example configuration for a server block:

server {
    listen 80;
    server_name yourdomain.com www.yourdomain.com;
    root /var/www/yourdomain.com/public_html;
    index index.html index.htm;
 
    # Location block for Perl scripts (.pl, .cgi)
    location ~ \.(pl|cgi)$ {
        try_files $uri =404; # Ensure the script file exists
        include /etc/nginx/fcgiwrap_params; # Include parameters
        fastcgi_pass unix:/var/run/fcgiwrap/fcgiwrap.sock; # Socket from service
        fastcgi_index index.pl;
    }
 
    # Location block for Ruby scripts (.rb)
    location ~ \.rb$ {
        try_files $uri =404;
        include /etc/nginx/fcgiwrap_params;
        fastcgi_pass unix:/var/run/fcgiwrap/fcgiwrap.sock;
    }
 
    # Location block for Bash/CGI scripts (.sh, .cgi)
    location ~ \.(sh|cgi)$ {
        try_files $uri =404;
        include /etc/nginx/fcgiwrap_params;
        fastcgi_pass unix:/var/run/fcgiwrap/fcgiwrap.sock;
    }
}

Important: After making changes to your Nginx configuration, always test it and then reload:

sudo nginx -t  # Tests configuration for syntax errors
sudo nginx -s reload  # OR, depending on your setup: sudo rc-service nginx reload

5. Testing the Setup with Scripts#

Let's create simple test scripts to verify everything works. Ensure all scripts are executable by the Nginx user.

  1. Create a test directory and script:
    sudo mkdir -p /var/www/yourdomain.com/public_html
    cd /var/www/yourdomain.com/public_html

5.1. Perl Script Test#

  1. Create test.pl:
    sudo nano test.pl
  2. Add this content:
    #!/usr/bin/env perl
     
    print "Content-type: text/html\n\n";
    print "<html><body>\n";
    print "<h1>Hello from Perl!</h1>\n";
    print "<p>This is a dynamic page generated by a Perl script.</p>\n";
    print "</body></html>\n";
  3. Make it executable:
    sudo chmod +x test.pl
    sudo chown nginx:nginx test.pl
  4. Access it via your browser: http://yourdomain.com/test.pl

5.2. Ruby Script Test#

  1. Create test.rb:
    sudo nano test.rb
  2. Add this content:
    #!/usr/bin/env ruby
     
    puts "Content-type: text/html\n\n"
    puts "<html><body>"
    puts "<h1>Hello from Ruby!</h1>"
    puts "<p>This is a dynamic page generated by a Ruby script.</p>"
    puts "</body></html>"
  3. Make it executable:
    sudo chmod +x test.rb
    sudo chown nginx:nginx test.rb
  4. Access it via your browser: http://yourdomain.com/test.rb

5.3. Bash Script Test#

  1. Create test.sh (or test.cgi):
    sudo nano test.sh
  2. Add this content:
    #!/bin/bash
     
    echo "Content-type: text/html"
    echo ""
    echo "<html><body>"
    echo "<h1>Hello from Bash!</h1>"
    echo "<p>This is a dynamic page generated by a Bash script.</p>"
    echo "<p>The current date is: $(date)</p>"
    echo "</body></html>"
  3. Make it executable:
    sudo chmod +x test.sh
    sudo chown nginx:nginx test.sh
  4. Access it via your browser: http://yourdomain.com/test.sh

If you see the "Hello from ..." messages, congratulations! FcgiWrap is successfully installed and configured.

6. Security Considerations#

  • File Permissions: Scripts must be owned and executable by the Nginx user, but should not be writable by it. This prevents code injection attacks.
  • Script Validation: FcgiWrap will execute any script with the configured extension. Be extremely careful with user-uploaded content. It's better to have upload directories handled by a dedicated application (like PHP) rather than allowing direct execution.
  • Limit Scope: Use the Nginx location blocks carefully. Don't use a broad regex like ~ \.cgi$ on a directory where users have write access unless you fully trust them.
  • SELinux/AppArmor: If you are using mandatory access control systems, you may need to create policies to allow Nginx to communicate with the FcgiWrap socket and for FcgiWrap to execute the interpreters.

7. Troubleshooting Common Issues#

  • 502 Bad Gateway: This usually means Nginx cannot communicate with FcgiWrap.
    • Is the fcgiwrap service running? Check its status.
    • Does the socket file (/var/run/fcgiwrap/fcgiwrap.sock) exist and have correct permissions? The Nginx user must have read/write access.
    • Check the Nginx error log: tail -f /var/log/nginx/error.log.
  • 403 Forbidden: This is a permission issue on the script file or one of the parent directories.
    • Ensure the script is executable (chmod +x).
    • Ensure the Nginx user has read (r) and execute (x) permissions on the script and all parent directories leading up to it.
  • Script Outputs Plain Text: The script is being executed, but the "Content-type" header is missing or incorrect. The browser is interpreting the output as plain text. Double-check that your script prints the header correctly (e.g., Content-type: text/html\n\n).

8. Conclusion#

You have successfully extended your Gentoo LEMP server beyond PHP. By installing and configuring FcgiWrap, you can now run dynamic web applications written in Perl, Ruby, and Bash. This setup provides a flexible and performant way to leverage the strengths of these languages within the robust Nginx environment. Remember to always prioritize security by carefully managing file permissions and being cautious about where and how you allow script execution.

9. References#