JustToThePoint English Website Version
JustToThePoint en español

Deploying Multiple Services with Portainer: Nginx, SearXNG, and Transmission & Troubleshooting

Mistakes are a great educator when one is honest enough to admit them and willing to learn from them, Aleksandr Solzhenitsynv

VPN

Portainer is a container management platform that provides a user-friendly interface for deploying, managing, and monitoring containerized applications —especially those running on Docker or Kubernetes.

This is the third post in our Portainer series, building on Deploying Portainer on Proxmox LXC. It is highly recommended that you read our first Deploying Portainer on Proxmox LXC: Script-Driven & GUI Walkthrough and second posts, Deploying WordPress as a Portainer Stack: A Hands-On Guide to Docker Compose.

Portainer GUI Management Benefits

  1. Visual Interface: Easier to see container status at a glance.
  2. Log Monitoring: Real-time log viewing with filtering options.
  3. Resource Usage: Monitor CPU, memory, and network usage.
  4. Quick Actions: One-click start/stop/restart operations.

Deploying Nginx via Portainer Stack

  1. Log into your Portainer Server instance by opening a web browser and going to: https://192.168.1.40:9443 (or your Portainer container’s IP).

  2. In the Portainer interface, click on the Stacks menu item located in the left sidebar. Then, press the Add stack button at the top-right corner of the Stacks page.

  3. Configure the Stack: Name: Provide a name for your stack, e.g., mynginx
    Compose File: You can either write your Docker Compose file in the provided editor or upload an existing file.
    Environment Variables: Optionally, define any environment variables needed for your services.

  4. Under Build method, choose Web editor. This lets you type out a Docker Compose file manually.

  5. In the big text box, paste your entire docker-compose.yml

    version: '3'
    
    services:
    nginx:
        image: nginx:latest
        ports:
        - "8080:80"
        volumes:
        - /var/www/html:/usr/share/nginx/html
        restart: always
    

    version: ‘3’ It specifies the version of the Docker Compose file format.
    services: It defines the different services (containers) that make up your application. In this case, there is one service named nginx.
    nginx: This is the name of the service, which will be used to create a container for running Nginx.
    image: nginx:latest This specifies the Docker image to use for the container. Here, it pulls the latest version of the official Nginx image from Docker Hub.
    ports: This section maps ports from the host to the container:
    - “8080:80” This maps port 8080 on the host machine to port 80 in the Nginx container. This means you can access the Nginx web server by visiting http://192.168.1.40:8080/ in a web browser.
    volumes: It defines data volumes to persist data and share files between the host and the container:
    - /var/www/html:/usr/share/nginx/html This mounts (read-only) the host directory /var/www/html to the container directory /usr/share/nginx/html.
    Any files placed in /var/www/html on the host will be served by Nginx, allowing you to easily manage web content.
    restart: always: This option ensures that the Nginx container automatically restarts if it stops or if the Docker daemon restarts.

  6. Prepare Host Content. Creating the index.html file on the host server (where Docker and Portainer are installed):

    mkdir -p /var/www/html
    cd /var/www/html
    
    nvim index.html
    
    <!DOCTYPE html>
    <html>
    <head>
        <title>Hello, Nginx!</title>
    </head>
    <body>
        <h1>Hello, Nginx!</h1>
        <p>This is served by Nginx in a Docker container.</p>
    </body>
    </html>
    
  7. Toggle Enable access control off. Click Deploy the stack.

  8. Access the Nginx web server by visiting http://192.168.1.40:8080/ in a web browser and you should see your test page.

SearXNG Web Server Stack for Portainer

This configuration will set up a SearXNG instance, a privacy-respecting search engine, using Docker. Log into your Portainer Server instance, click on the Stacks menu item, then press the Add stack button at the top-right corner of the Stacks page.

Configure the Stack: Name: Provide a name for your stack, e.g., searxnx, and write your Docker Compose file in the provided editor: Build method, Web editor.

# It specifies the version of the Docker Compose file format.
version: '3.8'

# It defines the services that will be run in the stack.
services:
  # This is the name of the service.
  searxng:
    # It specifies the Docker image to use, in this case, searxng/searxng.
    # A well maintained Docker image. SearXNG is a metasearch engine. Users are neither tracked nor profiled.
    image: searxng/searxng
    # Sets a custom name for the container (e.g., SearXNG).
    container_name: SearXNG
    # Defines how to check the health of the container.
    healthcheck:
      # Command to run for the health check.
      # wget is used to check if our webserver is responsive, -no-verbose suppresses output
      # --tries=1 tells wget to only attempt the connection once, --spider instead of using wget for downloading the content, it only checks if the resource is available or not.
      test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:8080/"]
      # How often to perform the health check
      interval: 10s
      # Time to wait for the health check to succeed
      timeout: 5s
      # Number of retries before considering the container unhealthy
      retries: 3
      # Initial startup period to allow the service to stabilize
      start_period: 90s
    # Sets a memory limit for the container (8 GB).
    # Adjust CPU and memory limits as needed.
    mem_limit: 8g
    # Assigns CPU share weight (2048) to the container.
    cpu_shares: 2048
    # Security options for the container.
    # no-new-privileges:true prevents the container from gaining additional privileges.
    security_opt:
      - no-new-privileges:true
    # Maps the host directory E:\searxng to the container's /etc/searxng, allowing read and write access.
    volumes:
      - E:\searxng:/etc/searxng:rw
    # Exposes port 8080 of the container on port 5147 of the host.
    ports:
      - 5147:8080
    # Defines the restart policy. on-failure:5 means the container will restart up to 5 times on failure.
    restart: on-failure:5

Toggle Enable access control off. Click Deploy the stack.

Once the container is running, you can access the SearXNG web interface by navigating to http://< your-host-ip >:5147 in your web browser.

image info

Transmission: Torrent Client

version: '3.7' # Specify the version of the Docker Compose file format, 3.7 is compatible with Docker Desktop.

# This section defines the services that will be deployed as part of the stack.
services:
  # This is the name of the service for the Transmission torrent client.
  transmission:
    image: lscr.io/linuxserver/transmission # Use the official LinuxServer Transmission image.
    # This is a well maintained and trusted image from LinuxServer.io.

    # Set a custom name for the container
    container_name: transmission

    environment:
      - PUID=0 # Set the user ID for the container (0 = root)
      - PGID=0 # Set the group ID for the container (0 = root)
      # On a Windows‐host bind-mount the only “owner” really is the Docker daemon (root in the container),
      # so dropping into a non-root user (PUID=1000) will often be blocked.
      - TZ=Europe/Madrid # Specify the timezone for the container

    volumes: # Defines persistent storage locations.
      - E:\transmission\config:/config
      # Map the host directory for configuration files where Transmission's configuration files will be stored.
      - E:\transmission\download:/download
      # Map the host directory where downloaded torrents will be saved.

    ports: # Maps the container's ports to the host's ports.
      - 9091:9091
      # Expose port 9091 for the web UI, allowing access to the Transmission interface through http://< your-host-ip >:9091.
      - 51413:51413 # Expose the TCP port 51413 for torrent data transfer.
      - 51413:51413/udp # Expose the UDP port 51413 for torrent data transfer.
      # This is necessary for certain peer connections.

    restart: unless-stopped
    # Ensure the container restarts unless it has been manually stopped

Mount into /downloads (plural) and run as root (PUID=0/PGID=0) to avoid Windows-NTFS permission issues.

Deploy and access the web UI at [http://] + dirIPContainer + [:9091/].

image info

Quick Actions in Portainer GUI

Once connected to Portainer, you can perform these container management actions:

Troubleshoting & best practices

  1. Basic Connectivity Testing. Test Connectivity from Another Machine.
    # Test HTTPS (default secure port)
    curl -k -I https://192.168.1.40:9443
    
    # Test HTTP (if enabled)
    curl -I http://192.168.1.40:9000
    
    # Test from browser
    # https://192.168.1.40:9443
    # http://192.168.1.40:9000
    
    # If connection fails or times out
    # Restart Portainer container
    docker restart portainer
    
    # If restart doesn't help, recreate with HTTP enabled
    docker stop portainer && docker rm portainer
    docker volume rm portainer_data
    docker volume create portainer_data
    # Use the HTTP Port as a Fallback
    # Portainer CE by default listens on 9443 for HTTPS.
    # If you want an unencrypted HTTP interface on 9000, you must add --http-enabled when running
    docker run -d \
    -p 8000:8000 \
    -p 9000:9000 \
    -p 9443:9443 \
    --name portainer \
    --restart=always \
    -v /var/run/docker.sock:/var/run/docker.sock \
    -v portainer_data:/data \
    portainer/portainer-ce:latest \
    --http-enabled
    
    # Test local connectivity within container
    # Test HTTPS locally
    curl -k -I https://127.0.0.1:9443
    
    # Test HTTP locally (if enabled)
    curl -I http://127.0.0.1:9000
    # Should return HTTP/1.1 200 OK
    
  2. Container Status and Log Inspection.
    # Access the LXC container (myportainer)
    pct enter 110
    
    # Verify all containers are running
    root@portainer:~# docker ps -a
    CONTAINER ID   IMAGE                           COMMAND                  CREATED       STATUS                   PORTS                                                                                                NAMES
    2b92263d8e90   wordpress:latest                "docker-entrypoint.s…"   8 hours ago   Up 8 hours               0.0.0.0:8880->80/tcp, [::]:8880->80/tcp                                                              mysql-wordpress-1
    e5704349f155   mysql:8.0                       "docker-entrypoint.s…"   8 hours ago   Up 8 hours               3306/tcp, 33060/tcp                                                                                  mysql-db-1
    # Expected output should show portainer container as "Up"
    bd7a95ca494c   portainer/portainer-ce:latest   "/portainer"             9 hours ago   Up 9 hours               0.0.0.0:8000->8000/tcp, [::]:8000->8000/tcp, 0.0.0.0:9443->9443/tcp, [::]:9443->9443/tcp, 9000/tcp   myportainer
    3ec276d311bf   hello-world                     "/hello"                 9 hours ago   Exited (0) 9 hours ago                                                                                                        vibrant_wozniak
    
    # Test HTTPS interface locally
    curl -k https://127.0.0.1:9443 -I
    
    HTTP/1.1 200 OK
    # Portainer is running correctly inside the container.
    [...]
    # Or test the HTTP fallback on 9000 if you have already enabled it
    curl -I http://127.0.0.1:9000
    
    # Inspect Portainer logs for errors
    docker logs portainer
    
    # Follow logs in real-time
    docker logs -f portainer
    
  3. Network Configuration Verification.
    # Verify container's IP configuration.
    # Ensure the container’s IPv4 address really is 192.168.1.40/24
    root@portainer:~# ip a show eth0
    # Expected output:
    2: eth0@if17:  mtu 1500 qdisc noqueue state UP group default qlen 1000
        link/ether bc:24:11:39:28:4c brd ff:ff:ff:ff:ff:ff link-netnsid 0
        inet 192.168.1.40/24 brd 192.168.1.255 scope global eth0
        valid_lft forever preferred_lft forever
        inet6 fe80::be24:11ff:fe39:284c/64 scope link proto kernel_ll
        valid_lft forever preferred_lft forever
    
    # Install net-tools if not available
    sudo apt update && apt install net-tools -y
    # Confirm Portainer is listening on all interfaces (0.0.0.0:9443):
    root@ubuntu-vpn:~# netstat -tuln | grep -E '9000|9443|8000'
    # Expected output:
    # tcp        0      0 0.0.0.0:9000            0.0.0.0:*               LISTEN
    # tcp        0      0 0.0.0.0:9443            0.0.0.0:*               LISTEN
    # tcp        0      0 0.0.0.0:8000            0.0.0.0:*               LISTEN
    
  4. Firewall Configuration.
    # Check UFW status within container
    root@portainer:~# sudo ufw status verbose
    Status: inactive
    # If active, allow Portainer ports
    sudo ufw allow 9443/tcp
    sudo ufw allow 9000/tcp
    sudo ufw allow 8000/tcp
    sudo ufw reload
    # Or disable UFW if not needed
    sudo ufw disable
    
  5. LXC Container Configuration. Review and verify container configuration:
    # From Proxmox host
    cat /etc/pve/lxc/110.conf
    # Key configuration element
    arch: amd64
    cores: 2
    # nesting=1: This features enables running containers within containers (essential for Docker)
    # keyctl=1: Allows key management operations  (it could be necessary for certain applications that rely on secure access),
    # mknod=1: while also enabling the creation of device files (this can be necessary for applications that require direct access to hardware or specific device files.).
    features: nesting=1,keyctl=1,mknod=1
    hostname: portainer
    memory: 4096
    nameserver: 1.1.1.1 8.8.8.8
    # Verify the LXC’s Network Mode
    # If the Container Firewall is enabled (indicated by firewall=1), it may restrict network access for the container.
    # If you encounter connectivity issues, consider disabling it by removing the firewall=1 setting in the container configuration.
    # type=veth specifies that the network interface is a virtual Ethernet (veth) interface.
    # Veth pairs are used in container networking to connect the container’s network namespace to the host’s network namespace.
    net0: name=eth0,bridge=vmbr0,gw=192.168.1.1,hwaddr=BC:24:11:39:28:4C,ip=192.168.1.40/24,type=veth
    onboot: 1
    ostype: ubuntu
    rootfs: mypool:subvol-110-disk-0,size=80G
    startup: 1
    swap: 512
    # This line sets the AppArmor profile for the container to "unconfined."
    # AppArmor is a Linux security module that restricts the capabilities of programs.
    # By using the "unconfined" profile, the container is given broader permissions and is not restricted by AppArmor's security policies.
    lxc.apparmor.profile: unconfined
    
    # Apply configuration changes:
    
    # Restart container after config changes
    pct restart 110
    
    # Verify from host
    pct exec 110 -- curl -k -I https://192.168.1.40:9443
    
  6. Check the Proxmox Host’s Firewall (if enabled). In the Proxmox Web UI, select your Node/Host (e.g., myserver), Firewall tab, Options, and ensure the firewall is disabled or configure rules. Add firewall rules if needed. Go to Datacenter, Firewall, Rules, Add a new rule to Accept ingress on TCP ports 9000, 9443 to the container’s IP (192.168.1.40):
    Direction: in (inbound)
    Action: ACCEPT
    Protocol: TCP
    Destination: Enter the IP address of your container: 192.168.1.40
    Destination port: 9000,9443,8000
    Enable: Yes (checked)
    Comment (optional): Add a note like “Allow Portainer ports” if desired. Interface: (Optional) Select the correct network interface if your container is on a specific bridge (e.g., vmbr0). If unsure, you can leave this blank.
    Source: (Optional) You can restrict the source IP or network if you want to limit access. If you want to allow access from any IP on your LAN, leave this blank.

    This will allow traffic to reach your container’s Portainer service.

  7. SSH Configuration. Once the container is fully provisioned, set up ssh access.
    # From your local machine:
    ssh-copy-id root@192.168.1.40.
    # Test SSH connection:
    ssh root@192.168.1.40
    
  8. Configure local DNS resolution. Log into Pi-hole’s web interface (e.g., http://192.168.1.43/admin), go to Settings, Local DNS Records, and add record: myportainer (domain) mapped to 192.168.1.40. Then, test ssh root@myportainer and create an alias:
    echo 'alias myportainer="ssh root@myportainer"' >> ~/.bashrc
    source ~/.bashrc
    
  9. Advanced Troubleshooting.
    # Check Docker service status
    systemctl status docker
    
    # Restart Docker service
    systemctl restart docker
    
    # Check Docker system information
    docker system info
    
    # Clean up Docker resources
    docker system prune -a
    
    # Check disk usage
    df -h
    docker system df
    
    # Monitor container resources
    docker stats portainer
    
    # Check container health
    docker inspect portainer | grep -A 10 "Health"
    
    # Test network connectivity from container
    ping -c 4 8.8.8.8
    
    # Check routing table
    ip route show
    
    # Test DNS resolution
    nslookup google.com
    
Bitcoin donation

JustToThePoint Copyright © 2011 - 2025 Anawim. ALL RIGHTS RESERVED. Bilingual e-books, articles, and videos to help your child and your entire family succeed, develop a healthy lifestyle, and have a lot of fun. Social Issues, Join us.

This website uses cookies to improve your navigation experience.
By continuing, you are consenting to our use of cookies, in accordance with our Cookies Policy and Website Terms and Conditions of use.