Cloudron makes it easy to run web apps like WordPress, Nextcloud, GitLab on your server. Find out more or install now.


Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Bookmarks
  • Search
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Brand Logo

Cloudron Forum

Apps | Demo | Docs | Install
  1. Cloudron Forum
  2. Discuss
  3. Guide for Adding Custom Nginx Directives to a Cloudron Application

Guide for Adding Custom Nginx Directives to a Cloudron Application

Scheduled Pinned Locked Moved Discuss
guide nginx
1 Posts 1 Posters 398 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • S Offline
    S Offline
    Sydney
    wrote on last edited by girish
    #1

    Introduction

    Cloudron applications are reverse-proxied using Nginx, a high-performance proxy and web server. Cloudron manages the Nginx configuration of applications themselves, but there may be cases where the end user needs to add custom configuration directives on an ad-hoc basis for a specific application. Attempting to modify the application nginx config directly at /etc/nginx/applications/[app-id]/[app-domain].conf is not feasible, since these configuration files are ephemeral, and are re-written upon restarts and Cloudron upgrades.

    However, I developed a workaround that allows users to add Nginx config snippets to Cloudron apps in a way that is persistent, and robust against being over-written. This method uses Nginx's include directives, a custom bash script, and a cronjob. This guide presents an overview of this workaround, and shares the custom bash script that I use.

    Disclaimer

    Before proceeding, be advised that Cloudron does not support ad-hoc user modifications to application Nginx configuration. This method is a workaround, and could result in broken reverse-proxy configurations should the user create an invalid Nginx configuration file.

    Method

    Nginx allows us to embed configuration file snippets using the include directive. We'll create a file at /etc/nginx/custom-nginx-directives.conf containing what we wish to include, and then include this within the application nginx config using include custom-nginx-directives.conf;. Note that include allows us to specify either relative, or absolute pathing. On Ubuntu Linux, relative paths are searched for starting from /etc/nginx.

    Once you have defined that file, we will use a bash script to add the line include custom-nginx-directives.conf; to the application nginx config. On Cloudron, the per-application nginx configuration are templated using EJS from cloudron/box/src/nginxconfig.ejs. The EJS template is complex and has many conditionals, but it has the following invariant structure (i.e. no matter what application, it will always have the following blocks):

    map $http_upgrade $connection_upgrade {
        # [Extranuous information removed]
    }
    
    map $upstream_http_referrer_policy $hrp {
        # [Extranuous information removed]
    }
    
    # http server
    server {
        # [Extranuous information removed]
    }
    
    # https server
    server {
        # [Extranuous information removed]
    }
    

    Observe how for every Cloudron app, it will always have two server blocks. The first one defines the HTTP (i.e. port 80) listener, and usually contains a redirect to HTTPS. The second one is the HTTPS listener (i.e. port 443), and contains the bulk of the application-specific logic.

    For my specific use case, I needed to include custom location directives for my Cloudron app. This probably represents the most common use case for custom nginx configuration. Hence we need to insert our include directive at the end of the second server block. This can be done by searching for the last closing bracket of the file.

    It is this invariant which we may take advantage of. The following bash commands will search for the last closing bracket in the nginx file, and use sed to insert a line right before it.

    nginx_config_file="/etc/nginx/applications/[app_id]/[app_domain].conf"
    include_line="include custom-nginx-directives.conf;"
    
    # Find the line number(s) of all the closing brackets (i.e. '}')
    grep_result=$(grep --line-number '}' "$nginx_config_file")
    
    # Grep returns output in the form line_numbers:match. Extract only the line numbers with 'cut'
    line_numbers=$(echo "$grep_result" | cut --delimiter=':' --fields=1)
    
    # Select the last line number using 'tail'
    last_bracket_line=$(echo "$line_numbers" | tail --lines=1)
    
    # Append the $include_line before the last closing bracket in the Nginx configuration file. 
    sed -i "${last_bracket_line}i $include_line" "$nginx_config_file"
    

    Bash Script

    We can take the above logic, and implement it in a bash script that does some additional checking and error prevention. We should first make sure that the line is not already present. Likewise, if the line is not present, and we include it, we should also automatically test and reload our nginx configuration so that the changes take effect. Taking the above considerations in mind, we yield the following final script:

    #!/bin/bash
    #
    # Copyright (c) 2024 Shen Zhou Hong
    # This code is released under the MIT License. https://mit-license.org/
    #
    # This Bash script checks if the line defined in $include_line is present
    # in the Nginx configuration file at $nginx_config_file. If the line is not 
    # found, it appends the line before the last closing bracket and tests the 
    # configuration using 'nginx -t'. If the test succeeds, it reloads Nginx with 
    # 'systemctl reload nginx'.
    #
    # This file is intended for use with Cloudron in order to add includes to
    # the nginx configuration of applications in a way that is robust against being
    # re-written. It should be run using a cronjob as root.
    #
    # Example crontab entry (*/30 specifies that it shall run every 30 minutes):
    # 
    # */30 * * * * /path/to/add_includes.sh 2>&1
    
    set -eEu -o pipefail
    
    # Make sure to use absolute pathing if this script is run as a cronjob.
    # Replace [app_id] and [app_domain] with actual values.
    nginx_config_file="/etc/nginx/applications/[app_id]/[app_domain].conf"
    # By default, Nginx's include directive takes either a relative, or an absolute
    # path. On Ubuntu relative paths are defined in relation to /etc/nginx
    include_line="include custom-nginx-directives.conf;"
    
    # Check if the Nginx configuration file exists
    if [ ! -f "$nginx_config_file" ]; then
        echo "Error: Nginx configuration file '$nginx_config_file' not found."
        exit 1
    fi
    
    # Check if the line is already present in the config file
    if grep -q "$include_line" "$nginx_config_file"; then
        echo "Line is already present in $nginx_config_file. No changes made to file."
        exit 0
    # If the include_line is not present, we will add it to the end of the file, right
    # before the final closing bracket (i.e. '}').
    else
        # Find the line number(s) of all the closing brackets (i.e. '}')
        grep_result=$(grep --line-number '}' "$nginx_config_file")
    
        # Grep returns output in the form line_numbers:match. Extract only the line numbers with 'cut'
        line_numbers=$(echo "$grep_result" | cut --delimiter=':' --fields=1)
    
        # Select the last line number using 'tail'
        last_bracket_line=$(echo "$line_numbers" | tail --lines=1)
    
        # Append the $include_line before the last closing bracket in the Nginx configuration file. 
        # The sed command with the 'i' operation specifies an insertion at the line number defined
        # in the $last_bracket_line.
    
        sed -i "${last_bracket_line}i $include_line" "$nginx_config_file"
        echo "Line added successfully to $nginx_config_file."
    
        # Test the Nginx configuration
        if nginx -t; then
            echo "Nginx configuration test successful. Reloading Nginx..."
            systemctl reload nginx
            echo "Nginx reloaded successfully."
        else
            echo "Nginx configuration test failed. Please check the configuration manually."
            exit 1
        fi
    fi
    

    Link to latest version on Github Gist

    Crontab

    Running the script will perform the include once. However, the changes made to /etc/nginx/applications will inevitably be lost upon restart, or platform upgrade. Hence, we will next define a crontab entry that will run the script frequently at a regular interval. Access your crontab as root via:

    sudo crontab -e
    

    Now add the following entry:

    */30 * * * * /path/to/add_includes.sh 2>&1
    

    The above crontab command will run the script every 30 minutes.

    Conclusion

    This guide presents a method of adding persistent custom nginx directives to Cloudron applications using a bash script and a crontab. Although it is not a very sophisticated approach, it works well enough for my use case, and I hope it will be useful for other users as well.

    In the future, I hope there will be a way for Cloudron to support custom Nginx directives, so that these workarounds are no longer necessary.

    1 Reply Last reply
    2
    Reply
    • Reply as topic
    Log in to reply
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes


    • Login

    • Don't have an account? Register

    • Login or register to search.
    • First post
      Last post
    0
    • Categories
    • Recent
    • Tags
    • Popular
    • Bookmarks
    • Search