noobdev
noobdev

Reputation: 1

point docker container to a host web server (nginx)

So I'm trying to install a GitLab instance inside my host. My host already have Nginx installed (without docker) and has other subdomains running fine.

My goal is to have them both running (if possible) but GitLab's docker container cant run while the host's web server (Nginx) is on, and the other way around. I end up with a docker error message below:

Error starting userland proxy: listen tcp 0.0.0.0:80: bind: address already in use.

Or at some point the port 443, and that's the Nginx ports running. My question is how do I point my container to run through my installed Nginx on my virtual host so I can visit my GitLab instance as a subdomain? My server is running ubuntu 16.04.

Upvotes: 0

Views: 178

Answers (1)

Toastlover
Toastlover

Reputation: 81

The Solution is Quite Simple after doing a bit of Research, For Security Purposes, you should setup a docker bridge network in which you put all your containers in (simplified ofc its even better to use different networks so containers can't see traffic from other containers but that may be a task for later on when you got more expirience.)

After you created the docker network, you should setup nginx proxy manager container for simplicity and configuring stuff via the dashboard, connect the internal docker containers to a domain by hostname to access it outside.

Docker Containers act as a completly different system, they won't be ableo to interact with an nginx server running on the host machine, you need to start a nginx server within the container, you also could start a seperate nginx container were all your containers connect to.

Long Story Short, here is the configuration of my running webserver, you only need to adapt it to your setup wich should be easy. Place the Following Files in the root path of your website at the host machine:

dockerfile:

# Use a base image with PHP-FPM and Nginx
FROM php:8-fpm

# Set locale environment variables
ENV LANG=C.UTF-8
ENV LC_ALL=C.UTF-8

# Easy installer allows for easier dependency installation
ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/

# Set the working directory inside the container
WORKDIR /var/www/html

# Copy the contents of the codebase into the container
COPY . .

# Create a new Nginx configuration file for the virtual host
COPY vhosts.conf /etc/nginx/sites-available/vhosts.conf
COPY opcache.ini /usr/local/etc/php/conf.d/opcache.ini

# Install Nginx and other dependencies
# Add Nginx to the Usergroup with the needed permissions
# Add Read Permissions to Nginx
# Enable the virtual host
# Give required Permission to start.sh
# Install php extensions/modules 
# && install-php-extensions curl gd xdebug mysqli pdo pdo_mysql memcache bz2 zip
RUN apt-get -y update \
    && apt-get -y upgrade \
    && apt-get install -y nginx fonts-noto-color-emoji \
    && rm -rf /var/lib/apt/lists/* \
    && groupadd -g 1001 nginx \
    && useradd -u 1001 -ms /bin/bash -g nginx nginx \
    && chown -R nginx:nginx /var /run \
    && ln -s /etc/nginx/sites-available/vhosts.conf /etc/nginx/sites-enabled/vhosts.conf \
    && chmod +x ./start.sh /usr/local/bin/install-php-extensions \
    && install-php-extensions curl mysqli memcache bz2 zip

# Expose port
EXPOSE 80

# Start Nginx and PHP-FPM
USER nginx
CMD ["./start.sh"]

start.sh

#!/bin/bash

echo "Logging Some Stuff..."
echo "---------------------"
echo "Validating nginx Configuration:"
nginx -t
nginx -t vhosts.conf

echo "..."
echo "---------------------"

echo "Starting Webserver..."
# Start PHP-FPM
php-fpm &

# Start Nginx
nginx -g "daemon off;"

vhosts.conf

  • server_name need to match the domain name you want to use.
  • fastcgi_pass need to match the hostname of the container were your nginx is running in (for me, it's the same container)
server {
    listen 80;
    server_name genefit.cc www.genefit.cc;
    root /var/www/html;
    index index.htm index.html index.php;

    charset utf-8;

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt { access_log off; log_not_found off; }

    access_log off;
    error_log /var/log/nginx/error.log error;

    sendfile off;

    client_max_body_size 100m;

    location ~* /(phpinfo\.php|php_info\.php|info\.php|_profiler/phpinfo\.php|\.ht) {
        deny all;
    }

    # Allow POST on specific endpoints
    location ~* ^/(route1/|route2/|route3/|route4/|route5/|route6/)$ {
        if ($request_method !~ ^(POST|GET)$) {
            return 405;
        }
    }

    # Block requests from certain user agents
    # Restrict methods other than GET and provide a 405 error for other locations
    location / {
        if ($http_user_agent ~* (HTTrack|wget|curl|Python)) {
            return 418;
        }
        if ($request_method !~ ^(GET)$) {
            return 405;
        }
    }

    # Block access to hidden files with custom error code
    location ~ /\. {
        return 444;
    }

    # Error page configuration
    error_page 403 404 405 418 444 500 /error.php?code=$status;

    location ~ /error.php$ {
        include fastcgi_params;
        root /var/www/html/;
        #try_files $uri $uri/ =404;
        fastcgi_pass genefit.cc:9000;
        fastcgi_index error.php;
        fastcgi_intercept_errors off;
        fastcgi_buffer_size 16k;
        fastcgi_buffers 4 16k;

        fastcgi_param SCRIPT_FILENAME $request_filename;
        fastcgi_param QUERY_STRING $query_string;
    }

    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass genefit.cc:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_intercept_errors on;
        fastcgi_buffer_size 16k;
        fastcgi_buffers 4 16k;
    }
}

docker-compose.yml:

version: '3.0'

services:
  genefit.cc:
    container_name: genefit.cc
    restart: always
    build:
      context: /var/www/html/genefit.cc
      dockerfile: dockerfile
    networks:
      - revproxy
    depends_on:
      - genefit_db

  genefit_db:
    container_name: genefit_db
    restart: always
    image: mysql:latest
    environment:
      - MYSQL_ROOT_PASSWORD=GENERATED_50CHAR_PASSWD
      - MYSQL_USER=u247182034_genefit
      - MYSQL_PASSWORD=GENERATED_50CHAR_PASSWD
      - MYSQL_DATABASE=u123_genefit
    ports:
      - "3306:3306"
    networks:
      - revproxy
    volumes:
      - db_data:/var/lib/mysql
      - ./u123_genefit.sql:/docker-entrypoint-initdb.d/u123_genefit.sql

networks:
  revproxy:
    external: true

volumes:
  db_data:

error.php

<?php
// Capture the error code from the query parameter or default to 500
$status_code = isset($_GET['code']) ? intval($_GET['code']) : 500;

// Static mapping of status codes to messages
$error_messages = [
    // Informational 1xx
    100 => "Continue: The server has received the request headers, and the client should proceed to send the request body. 🚀 (Proceed... if you dare.)",
    101 => "Switching Protocols: The server is switching protocols as requested by the client. 🔄 (Like switching lanes on a highway, but less fun.)",
    102 => "Processing: The server has received and is processing the request, but no response is available yet. â³ (Patience is a virtue... they say.)",

    // Success 2xx
    200 => "OK: The request was successful and the server responded with the requested data. 👠(For now, everything is fine...)",
    201 => "Created: The request was successful and a new resource was created. 🎉 (Another one bites the dust!)",
    202 => "Accepted: The request has been accepted for processing, but the processing is not complete. 🕒 (Your request is in the queue... somewhere.)",
    203 => "Non-Authoritative Information: The server is providing information from another source. 🤔 (Trust issues? Maybe.)",
    204 => "No Content: The server successfully processed the request, but there's nothing to show for it. 👠(Emptiness. Like your soul after a long day.)",
    205 => "Reset Content: Please reset the document view or something... 🔄 (Like hitting the reset button on life.)",
    206 => "Partial Content: Only part of the requested data is available. 🥧 (A slice of the cake, not the whole bakery.)",

    // Redirection 3xx
    300 => "Multiple Choices: Multiple options are available, pick your poison. âš–ï¸ (Choose wisely, or don't...)",
    301 => "Moved Permanently: This resource has a new home. Good luck finding it! 🔗 (Catch me if you can!)",
    302 => "Found: The requested resource is temporarily elsewhere. 🔠(Lost and found, mostly lost.)",
    303 => "See Other: Look somewhere else for what you need. ðŸ•µï¸ (Hint: It's not here.)",
    304 => "Not Modified: No new updates. You're not missing much. 🛑 (Still the same, just like yesterday.)",
    305 => "Use Proxy: Access denied. Use a proxy. ðŸ•µï¸ (Because we love middlemen.)",
    307 => "Temporary Redirect: Go this way for now. 🚦 (Like being detoured in a dark alley.)",
    308 => "Permanent Redirect: This resource has moved forever. 🔗 (It wasn't you, it was them.)",

    // Client Error 4xx
    400 => "Bad Request: The server can't handle your nonsense. 🤦 (Next time, try making sense.)",
    401 => "Unauthorized: No entry without proper credentials. 🛂 (Looks like you're not on the list.)",
    402 => "Payment Required: Pay up, or move along. 💸 (Money talks, and you... don't.)",
    403 => "Forbidden: You shall not pass! 🚫 (Access denied, just like that one club.)",
    404 => "Not Found: What you're looking for is probably gone. 🔠(Or maybe it never existed...)",
    405 => "Method Not Allowed: This method won't work here. 🚫 (Like using a spoon to cut steak.)",
    406 => "Not Acceptable: The server can't provide the requested format. 😕 (Your standards are too high.)",
    407 => "Proxy Authentication Required: First, prove you're worthy... 🔒 (And then, maybe, we'll let you in.)",
    408 => "Request Timeout: Time's up! â²ï¸ (Life is short, so are timeouts.)",
    409 => "Conflict: Your request conflicts with the current state. 🥊 (Just like holiday family dinners.)",
    410 => "Gone: This resource is gone, like a ghost. âš°ï¸ (You weren't fast enough.)",
    411 => "Length Required: We need to know how long it is. 📠(Size does matter, apparently.)",
    412 => "Precondition Failed: You didn't meet the conditions. 🥺 (Just like those job applications...)",
    413 => "Payload Too Large: Your request is too hefty. 🔠(Try cutting down on the baggage.)",
    414 => "URI Too Long: Your link is way too long. 📠(Ever heard of tinyurl?)",
    415 => "Unsupported Media Type: The server can't process this type. 📼 (Old school formats not welcome.)",
    416 => "Requested Range Not Satisfiable: We can't serve that portion. 📠(You're asking for too much.)",
    417 => "Expectation Failed: Life is full of disappointments, isn't it? 📉 (Lower your expectations.)",
    418 => "I'm a teapot: I refuse to brew coffee because I'm a teapot. 🵠(I'm not what you think I am...)",
    421 => "Misdirected Request: This server can't help you. 🤦 (Wrong place, wrong time.)",
    422 => "Unprocessable Entity: We got your request, but it's gibberish. 🤷 (Nice try, though.)",
    423 => "Locked: This resource is locked, just like your phone after too many failed attempts. 🔒",
    424 => "Failed Dependency: Another request failed, so this one can't go through. 📉 (Domino effect in action.)",
    425 => "Too Early: Too soon! Wait a bit. 🢠(Patience, grasshopper.)",
    426 => "Upgrade Required: Time to move up to something better. â¬†ï¸ (Can't stay in the stone age forever.)",
    428 => "Precondition Required: Do the pre-work before trying this. 📠(No shortcuts here.)",
    429 => "Too Many Requests: Slow down, turbo. 🛑 (Even machines have limits.)",
    431 => "Request Header Fields Too Large: Your headers are overweight. 📬 (Slim them down.)",
    451 => "Unavailable For Legal Reasons: This content is banned. ðŸ›ï¸ (You can't handle the truth.)",

    // Server Error 5xx
    500 => "Internal Server Error: Oops! Something went terribly wrong. 💥 (The server is as confused as you are.)",
    501 => "Not Implemented: The server can't do that. 🚫 (Not in this lifetime, anyway.)",
    502 => "Bad Gateway: The server got a bad response from another server. 🚪 (Blame it on the upstream.)",
    503 => "Service Unavailable: The server is currently out of service. 😴 (Taking a break, like everyone should.)",
    504 => "Gateway Timeout: The server took too long to respond. â° (Slow servers, slow responses.)",
    505 => "HTTP Version Not Supported: The server doesn't speak that version. 📜 (Old language, new problems.)",
    511 => "Network Authentication Required: Authenticate yourself to proceed. 🔠(Who are you, again?)"
];

// Get the error message based on the status code
$error_message = isset($error_messages[$status_code]) ? $error_messages[$status_code] : "An unexpected error occurred.";

// Determine if the image should be shown
$show_image = isset($error_messages[$status_code]);
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Error <?php echo $status_code; ?></title>
    <style>
        /* Material Design Base */
        @import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap');

        body {
            margin: 0;
            padding: 0;
            background: linear-gradient(135deg, #ff416c, #ff4b2b);
            font-family: 'Roboto', sans-serif;
            display: flex;
            align-items: center;
            justify-content: center;
            height: 100vh;
            overflow: hidden;
            color: #fff;
        }

        .container {
            text-align: center;
            background: #fff;
            padding: 3rem;
            border-radius: 12px;
            box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
            animation: zoomIn 1s ease-out;
            max-width: 100%;
            width: 500px; /* Default width for larger screens */
            height: auto;
            box-sizing: border-box; /* Ensures padding is included in width calculation */
        }

        .container h1 {
            font-size: 4.5rem;
            margin: 0;
            color: #e74c3c;
            animation: bounceGlow 2s ease-in-out infinite;
            text-shadow: 0 0 10px rgba(255, 0, 0, 0.8);
        }

        .container p {
            font-size: 1.25rem;
            margin: 1.5rem 0;
            color: #333;
            opacity: 0.8;
        }

        .container img {
            max-width: 100%;
            height: auto;
            border-radius: 8px;
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
        }

        .button {
            display: inline-block;
            padding: 0.75rem 1.5rem;
            font-size: 1rem;
            font-weight: 500;
            color: #fff;
            background-color: #6200ea;
            border: none;
            border-radius: 4px;
            text-decoration: none;
            cursor: pointer;
            transition: background-color 0.3s ease, transform 0.2s ease;
            text-transform: uppercase;
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
        }

        .button:hover {
            background-color: #3700b3;
            transform: translateY(-2px);
        }

        .button:active {
            background-color: #03dac6;
            transform: translateY(0);
        }

        @keyframes zoomIn {
            from {
                transform: scale(0.8);
                opacity: 0;
            }
            to {
                transform: scale(1);
                opacity: 1;
            }
        }

        @keyframes bounceGlow {
            0%, 100% {
                transform: translateY(0);
                text-shadow: 0 0 10px rgba(255, 0, 0, 0.8);
            }
            50% {
                transform: translateY(-20px);
                text-shadow: 0 0 20px rgba(255, 0, 0, 0.8);
            }
        }

        /* Media Queries for Responsive Design */
        @media (max-width: 768px) {
            .container {
                width: calc(100% - 20px); /* 10px padding on each side */
                max-width: calc(100% - 20px);
            }

            .container h1 {
                font-size: 3rem;
            }

            .container p {
                font-size: 1rem;
            }

            .button {
                font-size: 0.875rem;
                padding: 0.5rem 1rem;
            }
        }

        @media (orientation: landscape) and (max-width: 768px) {
            .container {
                width: calc(100% - 20px); /* Adjust for landscape mode */
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>Error <?php echo $status_code; ?></h1>
        <p><?php echo htmlspecialchars($error_message, ENT_QUOTES, 'UTF-8'); ?></p>
        <?php if ($show_image): ?>
            <img src="https://http.cat/<?php echo $status_code; ?>" alt="Error <?php echo $status_code; ?>">
        <?php endif; ?>
        <a href="/" class="button">Go to Homepage</a>
        </br></br>
        <input type="button" class="button" value="Join our Discord Guild" onclick="window.open('https://genefit.top/discord','popUpWindow','height=400,width=600,left=10,top=10,,scrollbars=yes,menubar=no'); return false;" />
    </div>
</body>
</html>

After importing the sql file, it is important that you enter the container and delete it, you don't want users to download it, right?

docker exec genefit.cc rm /var/www/html/u123_genefit.sql

If this solution helped you, a upvote and mark as solved would be appriciated.

With this method, you also don't need to worry about already used ports as the ports will only be exposed within the container. This means that multiple containers can use the same port in the same docker network, esenteally allowing you to set every port to 80

Yours Truly, Toastlover

Upvotes: 0

Related Questions