{"id":20129,"date":"2026-05-15T15:39:07","date_gmt":"2026-05-15T12:39:07","guid":{"rendered":"https:\/\/unihost.com\/help\/?p=20129"},"modified":"2026-05-15T15:39:07","modified_gmt":"2026-05-15T12:39:07","slug":"publish-docker-service-https-traefik","status":"publish","type":"post","link":"https:\/\/unihost.com\/help\/publish-docker-service-https-traefik\/","title":{"rendered":"How to Publish a Docker Service via HTTPS on a VPS Using Traefik"},"content":{"rendered":"<p data-start=\"65\" data-end=\"348\">Docker services that accept HTTP requests usually listen on an internal container port: for example, 3000, 8080, 5000, or 8000. This port is used inside the Docker network, while external access to the service is better organized through a domain, HTTPS, and a reverse proxy.<\/p>\n<p data-start=\"350\" data-end=\"409\">In a production environment, the user opens the service at:<\/p>\n<pre><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\">https:\/\/app.example.com<\/span><\/pre>\n<p data-start=\"448\" data-end=\"578\">The domain points to the public IP address of the VPS, Traefik accepts the request and forwards it to the internal Docker service.<\/p>\n<p data-start=\"580\" data-end=\"607\">The scheme looks like this:<\/p>\n<pre><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\">User \u2192 https:\/\/app.example.com \u2192 Traefik \u2192 Docker service<\/span><\/pre>\n<h2><span style=\"font-family: Ubuntu, sans-serif; font-size: 12pt;\"><b>1. Point the Domain to the VPS<\/b><\/span><\/h2>\n<p data-start=\"715\" data-end=\"770\">First, create a DNS record for the domain or subdomain.<\/p>\n<p data-start=\"772\" data-end=\"780\">Example:<\/p>\n<pre><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\">Type: A<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\">Name: app<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\">Value: SERVER_IP<\/span><\/pre>\n<p>If the domain is example.com, the service will be available at:<\/p>\n<pre><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\">app.example.com<\/span><\/pre>\n<p>You can check DNS with the command:<\/p>\n<pre><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\">dig app.example.com +short<\/span><\/pre>\n<p>Expected result:<\/p>\n<pre><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\">SERVER_IP<\/span><\/pre>\n<h2><span style=\"font-family: Ubuntu, sans-serif; font-size: 12pt;\"><b>2. Prepare the Docker Service<\/b><\/span><\/h2>\n<p data-start=\"1078\" data-end=\"1142\">Assume the application runs inside the container on port 3000.<\/p>\n<p data-start=\"1144\" data-end=\"1171\">Example of a basic service:<\/p>\n<pre><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\">services:<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0app:<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0\u00a0\u00a0image: your-app-image:latest<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0\u00a0\u00a0container_name: app<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0\u00a0\u00a0restart: unless-stopped<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0\u00a0\u00a0expose:<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0\u00a0\u00a0\u00a0\u00a0- \"3000\"<\/span><\/pre>\n<p data-start=\"1315\" data-end=\"1499\">Here, expose is used instead of ports because the application does not need to be exposed directly to the internet. It will be available only inside the Docker network for Traefik.<\/p>\n<p data-start=\"1501\" data-end=\"1505\">Bad:<\/p>\n<pre><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\">ports:<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\">\u00a0\u00a0- \"3000:3000\"<\/span><\/pre>\n<p>Better:<\/p>\n<pre><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\">expose:<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\">\u00a0\u00a0- \"3000\"<\/span><\/pre>\n<p data-start=\"1584\" data-end=\"1715\"><strong>Important<\/strong><br data-start=\"1593\" data-end=\"1596\" \/>Do not publish the internal application port to the outside if access should go only through HTTPS and a reverse proxy.<\/p>\n<h2><span style=\"font-family: Ubuntu, sans-serif; font-size: 12pt;\"><b>3. Add Traefik as a Reverse Proxy<\/b><\/span><\/h2>\n<p data-start=\"1755\" data-end=\"1860\">Below is an example of a docker-compose.yml file where Traefik publishes the application through HTTPS.<\/p>\n<p data-start=\"1862\" data-end=\"1870\">Replace:<\/p>\n<p><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\">app.example.com<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\">admin@example.com<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\">your-app-image:latest<\/span><\/p>\n<p>with your own values.<\/p>\n<pre><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\">services:<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0traefik:<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0\u00a0\u00a0image: traefik:v3.0<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0\u00a0\u00a0container_name: traefik<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0\u00a0\u00a0restart: unless-stopped<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0\u00a0\u00a0command:<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0\u00a0\u00a0\u00a0\u00a0- \"--providers.docker=true\"<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0\u00a0\u00a0\u00a0\u00a0- \"--providers.docker.exposedbydefault=false\"<\/span><br \/><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0\u00a0\u00a0\u00a0\u00a0- \"--entrypoints.web.address=:80\"<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0\u00a0\u00a0\u00a0\u00a0- \"--entrypoints.websecure.address=:443\"<\/span><br \/><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0\u00a0\u00a0\u00a0\u00a0- \"--entrypoints.web.http.redirections.entrypoint.to=websecure\"<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0\u00a0\u00a0\u00a0\u00a0- \"--entrypoints.web.http.redirections.entrypoint.scheme=https\"<\/span><br \/><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0\u00a0\u00a0\u00a0\u00a0- \"--certificatesresolvers.letsencrypt.acme.email=admin@example.com\"<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0\u00a0\u00a0\u00a0\u00a0- \"--certificatesresolvers.letsencrypt.acme.storage=\/letsencrypt\/acme.json\"<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0\u00a0\u00a0\u00a0\u00a0- \"--certificatesresolvers.letsencrypt.acme.httpchallenge=true\"<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0\u00a0\u00a0\u00a0\u00a0- \"--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web\"<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0\u00a0\u00a0ports:<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0\u00a0\u00a0\u00a0\u00a0- \"80:80\"<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0\u00a0\u00a0\u00a0\u00a0- \"443:443\"<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0\u00a0\u00a0volumes:<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0\u00a0\u00a0\u00a0\u00a0- \"\/var\/run\/docker.sock:\/var\/run\/docker.sock:ro\"<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0\u00a0\u00a0\u00a0\u00a0- \".\/letsencrypt:\/letsencrypt\"<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0\u00a0\u00a0networks:<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0\u00a0\u00a0\u00a0\u00a0- proxy<br \/><\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0app:<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0\u00a0\u00a0image: your-app-image:latest<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0\u00a0\u00a0container_name: app<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0\u00a0\u00a0restart: unless-stopped<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0\u00a0\u00a0expose:<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0\u00a0\u00a0\u00a0\u00a0- \"3000\"<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0\u00a0\u00a0labels:<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0\u00a0\u00a0\u00a0\u00a0- \"traefik.enable=true\"<\/span><br \/><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0\u00a0\u00a0\u00a0\u00a0- \"traefik.http.routers.app.rule=Host(`app.example.com`)\"<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0\u00a0\u00a0\u00a0\u00a0- \"traefik.http.routers.app.entrypoints=websecure\"<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0\u00a0\u00a0\u00a0\u00a0- \"traefik.http.routers.app.tls=true\"<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0\u00a0\u00a0\u00a0\u00a0- \"traefik.http.routers.app.tls.certresolver=letsencrypt\"<\/span><br \/><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0\u00a0\u00a0\u00a0\u00a0- \"traefik.http.services.app.loadbalancer.server.port=3000\"<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0\u00a0\u00a0networks:<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0\u00a0\u00a0\u00a0\u00a0- proxy<\/span><br \/><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\">networks:<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0proxy:<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0\u00a0\u00a0name: proxy<\/span><\/pre>\n<p>Traefik uses routers, services, and entrypoints to process HTTP requests. In a Docker scenario, these parameters are usually set through container labels.<\/p>\n<h2><span style=\"font-family: Ubuntu, sans-serif; font-size: 12pt;\"><b>4. What This Configuration Does<\/b><\/span><\/h2>\n<p>In this example, Traefik:<\/p>\n<ul>\n<li>listens on ports 80 and 443;<\/li>\n<li>gets information about containers through the Docker socket;<\/li>\n<li>does not publish all containers automatically;<\/li>\n<li>looks only for containers with traefik.enable=true;<\/li>\n<li>accepts requests for app.example.com;<\/li>\n<li>issues an HTTPS certificate through Let\u2019s Encrypt;<\/li>\n<li>proxies requests to the app:3000 container.<\/li>\n<\/ul>\n<p>Key block for the application:<\/p>\n<pre><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\">labels:<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0- \"traefik.enable=true\"<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0- \"traefik.http.routers.app.rule=Host(`app.example.com`)\"<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0- \"traefik.http.routers.app.entrypoints=websecure\"<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0- \"traefik.http.routers.app.tls=true\"<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\"> \u00a0- \"traefik.http.routers.app.tls.certresolver=letsencrypt\"<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\">\u00a0\u00a0- \"traefik.http.services.app.loadbalancer.server.port=3000\"<\/span><\/pre>\n<p>What this means:<\/p>\n<pre><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\">traefik.enable=true<\/span><\/pre>\n<p>Traefik should process this container.<\/p>\n<pre><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\">Host(`app.example.com`)<\/span><\/pre>\n<p>Requests to this domain are routed to the container.<\/p>\n<pre><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\">entrypoints=websecure<\/span><\/pre>\n<p>The service works through the HTTPS entrypoint, that is, through port <code data-start=\"4620\" data-end=\"4625\">443<\/code>.<\/p>\n<pre><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\">tls.certresolver=letsencrypt<\/span><\/pre>\n<p data-start=\"4670\" data-end=\"4731\">A Let\u2019s Encrypt certificate should be issued for this domain.<\/p>\n<pre><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\">loadbalancer.server.port=3000<\/span><\/pre>\n<p>The internal application port inside the container is 3000.<\/p>\n<p>Traefik supports ACME\/Let\u2019s Encrypt for automatic certificate issuance. In the official Traefik Docker Compose example with Let\u2019s Encrypt, the certificate is issued through an HTTP challenge for a service published through Traefik.<\/p>\n<h2><span style=\"font-family: Ubuntu, sans-serif; font-size: 12pt;\"><b>5. Start the Services<\/b><\/span><\/h2>\n<p>Create a folder for certificates:<\/p>\n<pre><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\">mkdir -p letsencrypt<\/span><\/pre>\n<p>Start the containers:<\/p>\n<pre><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\">docker compose up -d<\/span><\/pre>\n<p>Check the status:<\/p>\n<pre><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\">docker ps<\/span><\/pre>\n<p>The following containers should be running:<\/p>\n<pre><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\">traefik<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\">app<\/span><\/pre>\n<p>Check Traefik logs:<\/p>\n<pre><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\">docker logs traefik<\/span><\/pre>\n<p>If DNS is configured correctly and ports 80\/443 are available, Traefik will be able to issue an HTTPS certificate.<\/p>\n<h2><span style=\"font-family: Ubuntu, sans-serif; font-size: 12pt;\"><b>6. Open Ports in the Firewall<\/b><\/span><\/h2>\n<p>The VPS must have these ports open:<\/p>\n<p><span style=\"font-family: Ubuntu, sans-serif; font-size: 12pt;\"><span style=\"font-weight: 400;\">80\/tcp<br \/><\/span><span style=\"font-weight: 400;\">443\/tcp<\/span><\/span><\/p>\n<p>If UFW is used:<\/p>\n<pre><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\">sudo ufw allow 80\/tcp<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\">sudo ufw allow 443\/tcp<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\">sudo ufw status<\/span><\/pre>\n<p>It is better to allow SSH only from a trusted IP:<\/p>\n<pre><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\">sudo ufw allow from YOUR_TRUSTED_IP to any port 22 proto tcp<\/span><\/pre>\n<h2><span style=\"font-family: Ubuntu, sans-serif; font-size: 12pt;\"><b>7. Check HTTPS<\/b><\/span><\/h2>\n<p>Open in the browser:<\/p>\n<pre><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\">https:\/\/app.example.com<\/span><\/pre>\n<p>You can also check it with curl:<\/p>\n<pre><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\">curl -I https:\/\/app.example.com<\/span><\/pre>\n<p>Expected result:<\/p>\n<pre><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\">HTTP\/2 200<\/span><\/pre>\n<p>or another successful response from the application.<\/p>\n<p><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\">\u0415\u0441\u043b\u0438 \u043e\u0442\u043a\u0440\u044b\u0442\u044c:<\/span><\/p>\n<pre><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\">http:\/\/app.example.com<\/span><\/pre>\n<p>Traefik should redirect the request to HTTPS because the configuration includes a redirect from web to websecure:<\/p>\n<pre><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\">- \"--entrypoints.web.http.redirections.entrypoint.to=websecure\"<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\">- \"--entrypoints.web.http.redirections.entrypoint.scheme=https\"<\/span><\/pre>\n<h2>Conclusion<\/h2>\n<p data-start=\"6403\" data-end=\"6494\">To publish a Docker service through HTTPS on a VPS, you can use Traefik as a reverse proxy.<\/p>\n<p data-start=\"6496\" data-end=\"6542\">In a simple setup, the scheme looks like this:<\/p>\n<p><span style=\"font-family: Ubuntu, sans-serif; font-size: 12pt; color: #1e1e1e; white-space-collapse: preserve;\">Domain \u2192 VPS \u2192 Traefik \u2192 Docker service<\/span><\/p>\n<p data-start=\"6597\" data-end=\"6658\">The application does not need to be exposed directly through:<\/p>\n<pre><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\">SERVER_IP:PORT<\/span><\/pre>\n<p data-start=\"6688\" data-end=\"6792\">It is enough to keep it inside the Docker network and publish only ports 80 and 443 through Traefik.<\/p>\n<p data-start=\"6794\" data-end=\"6991\">Traefik is convenient for Docker services because routing is described directly in docker-compose.yml through labels. This is especially useful if you need to publish several services on one VPS:<\/p>\n<pre><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\">app.example.com \u2192 app:3000<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\">api.example.com \u2192 api:8080<\/span><br \/><span style=\"font-weight: 400; font-family: Ubuntu, sans-serif; font-size: 12pt;\">panel.example.com \u2192 panel:9000<\/span><\/pre>\n<p>This approach is safer and more convenient for a production setup: the user opens the service through a domain with HTTPS, while the internal application port remains hidden.<\/p>\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Docker services that accept HTTP requests usually listen on an internal container port: for example, 3000, 8080, 5000, or 8000. This port is used inside the Docker network, while external access to the service is better organized through a domain, HTTPS, and a reverse proxy. In a production environment, the user opens the service at: [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[520,604],"class_list":["post-20129","post","type-post","status-publish","format-standard","hentry","tag-dedicated-servers","tag-vps-2"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.3 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>How to Publish a Docker Service via HTTPS<\/title>\n<meta name=\"description\" content=\"Learn how to publish a Docker service via HTTPS on a VPS using Traefik, DNS, Docker labels, Let\u2019s Encrypt, firewall rules, and reverse proxy routing.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/unihost.com\/help\/publish-docker-service-https-traefik\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"How to Publish a Docker Service via HTTPS\" \/>\n<meta property=\"og:description\" content=\"Learn how to publish a Docker service via HTTPS on a VPS using Traefik, DNS, Docker labels, Let\u2019s Encrypt, firewall rules, and reverse proxy routing.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/unihost.com\/help\/publish-docker-service-https-traefik\/\" \/>\n<meta property=\"og:site_name\" content=\"Unihost.FAQ\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/unihost\/\" \/>\n<meta property=\"article:published_time\" content=\"2026-05-15T12:39:07+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/unihost.com\/help\/minio.php?.\/unihost-logo-alt.png\" \/>\n\t<meta property=\"og:image:width\" content=\"250\" \/>\n\t<meta property=\"og:image:height\" content=\"141\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Unihost Support\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@unihost\" \/>\n<meta name=\"twitter:site\" content=\"@unihost\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Unihost Support\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"3 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/unihost.com\/help\/publish-docker-service-https-traefik\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/unihost.com\/help\/publish-docker-service-https-traefik\/\"},\"author\":{\"name\":\"Unihost Support\",\"@id\":\"https:\/\/unihost.com\/help\/#\/schema\/person\/bb5ae95f38577c920e6a7507888b715a\"},\"headline\":\"How to Publish a Docker Service via HTTPS on a VPS Using Traefik\",\"datePublished\":\"2026-05-15T12:39:07+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/unihost.com\/help\/publish-docker-service-https-traefik\/\"},\"wordCount\":630,\"publisher\":{\"@id\":\"https:\/\/unihost.com\/help\/#organization\"},\"keywords\":[\"dedicated servers\",\"VPS\"],\"inLanguage\":\"en\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/unihost.com\/help\/publish-docker-service-https-traefik\/\",\"url\":\"https:\/\/unihost.com\/help\/publish-docker-service-https-traefik\/\",\"name\":\"How to Publish a Docker Service via HTTPS\",\"isPartOf\":{\"@id\":\"https:\/\/unihost.com\/help\/#website\"},\"datePublished\":\"2026-05-15T12:39:07+00:00\",\"description\":\"Learn how to publish a Docker service via HTTPS on a VPS using Traefik, DNS, Docker labels, Let\u2019s Encrypt, firewall rules, and reverse proxy routing.\",\"breadcrumb\":{\"@id\":\"https:\/\/unihost.com\/help\/publish-docker-service-https-traefik\/#breadcrumb\"},\"inLanguage\":\"en\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/unihost.com\/help\/publish-docker-service-https-traefik\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/unihost.com\/help\/publish-docker-service-https-traefik\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Unihost\",\"item\":\"https:\/\/unihost.com\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Help\",\"item\":\"https:\/\/unihost.com\/help\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"How to Publish a Docker Service via HTTPS on a VPS Using Traefik\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/unihost.com\/help\/#website\",\"url\":\"https:\/\/unihost.com\/help\/\",\"name\":\"Unihost.FAQ\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\/\/unihost.com\/help\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/unihost.com\/help\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/unihost.com\/help\/#organization\",\"name\":\"Unihost\",\"alternateName\":\"Unihost\",\"url\":\"https:\/\/unihost.com\/help\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en\",\"@id\":\"https:\/\/unihost.com\/help\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/unihost.com\/help\/minio.php?2026\/01\/minio.png\",\"contentUrl\":\"https:\/\/unihost.com\/help\/minio.php?2026\/01\/minio.png\",\"width\":300,\"height\":300,\"caption\":\"Unihost\"},\"image\":{\"@id\":\"https:\/\/unihost.com\/help\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/unihost\/\",\"https:\/\/x.com\/unihost\",\"https:\/\/www.instagram.com\/unihost\/?hl=en\",\"https:\/\/www.linkedin.com\/company\/unihost-com\",\"https:\/\/www.youtube.com\/channel\/UCITKsxMDnslQY8brN3advgw\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/unihost.com\/help\/#\/schema\/person\/bb5ae95f38577c920e6a7507888b715a\",\"name\":\"Unihost Support\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en\",\"@id\":\"https:\/\/unihost.com\/help\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/a0c9db17c2a0d93e8a0d5ac123f8c5db750ad4d3d5657369c0c4e480f5af77b8?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/a0c9db17c2a0d93e8a0d5ac123f8c5db750ad4d3d5657369c0c4e480f5af77b8?s=96&d=mm&r=g\",\"caption\":\"Unihost Support\"},\"sameAs\":[\"https:\/\/unihost.com\/\"],\"url\":\"https:\/\/unihost.com\/help\/author\/support\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"How to Publish a Docker Service via HTTPS","description":"Learn how to publish a Docker service via HTTPS on a VPS using Traefik, DNS, Docker labels, Let\u2019s Encrypt, firewall rules, and reverse proxy routing.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/unihost.com\/help\/publish-docker-service-https-traefik\/","og_locale":"en_US","og_type":"article","og_title":"How to Publish a Docker Service via HTTPS","og_description":"Learn how to publish a Docker service via HTTPS on a VPS using Traefik, DNS, Docker labels, Let\u2019s Encrypt, firewall rules, and reverse proxy routing.","og_url":"https:\/\/unihost.com\/help\/publish-docker-service-https-traefik\/","og_site_name":"Unihost.FAQ","article_publisher":"https:\/\/www.facebook.com\/unihost\/","article_published_time":"2026-05-15T12:39:07+00:00","og_image":[{"width":250,"height":141,"url":"https:\/\/unihost.com\/help\/minio.php?.\/unihost-logo-alt.png","type":"image\/png"}],"author":"Unihost Support","twitter_card":"summary_large_image","twitter_creator":"@unihost","twitter_site":"@unihost","twitter_misc":{"Written by":"Unihost Support","Est. reading time":"3 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/unihost.com\/help\/publish-docker-service-https-traefik\/#article","isPartOf":{"@id":"https:\/\/unihost.com\/help\/publish-docker-service-https-traefik\/"},"author":{"name":"Unihost Support","@id":"https:\/\/unihost.com\/help\/#\/schema\/person\/bb5ae95f38577c920e6a7507888b715a"},"headline":"How to Publish a Docker Service via HTTPS on a VPS Using Traefik","datePublished":"2026-05-15T12:39:07+00:00","mainEntityOfPage":{"@id":"https:\/\/unihost.com\/help\/publish-docker-service-https-traefik\/"},"wordCount":630,"publisher":{"@id":"https:\/\/unihost.com\/help\/#organization"},"keywords":["dedicated servers","VPS"],"inLanguage":"en"},{"@type":"WebPage","@id":"https:\/\/unihost.com\/help\/publish-docker-service-https-traefik\/","url":"https:\/\/unihost.com\/help\/publish-docker-service-https-traefik\/","name":"How to Publish a Docker Service via HTTPS","isPartOf":{"@id":"https:\/\/unihost.com\/help\/#website"},"datePublished":"2026-05-15T12:39:07+00:00","description":"Learn how to publish a Docker service via HTTPS on a VPS using Traefik, DNS, Docker labels, Let\u2019s Encrypt, firewall rules, and reverse proxy routing.","breadcrumb":{"@id":"https:\/\/unihost.com\/help\/publish-docker-service-https-traefik\/#breadcrumb"},"inLanguage":"en","potentialAction":[{"@type":"ReadAction","target":["https:\/\/unihost.com\/help\/publish-docker-service-https-traefik\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/unihost.com\/help\/publish-docker-service-https-traefik\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Unihost","item":"https:\/\/unihost.com\/"},{"@type":"ListItem","position":2,"name":"Help","item":"https:\/\/unihost.com\/help\/"},{"@type":"ListItem","position":3,"name":"How to Publish a Docker Service via HTTPS on a VPS Using Traefik"}]},{"@type":"WebSite","@id":"https:\/\/unihost.com\/help\/#website","url":"https:\/\/unihost.com\/help\/","name":"Unihost.FAQ","description":"","publisher":{"@id":"https:\/\/unihost.com\/help\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/unihost.com\/help\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en"},{"@type":"Organization","@id":"https:\/\/unihost.com\/help\/#organization","name":"Unihost","alternateName":"Unihost","url":"https:\/\/unihost.com\/help\/","logo":{"@type":"ImageObject","inLanguage":"en","@id":"https:\/\/unihost.com\/help\/#\/schema\/logo\/image\/","url":"https:\/\/unihost.com\/help\/minio.php?2026\/01\/minio.png","contentUrl":"https:\/\/unihost.com\/help\/minio.php?2026\/01\/minio.png","width":300,"height":300,"caption":"Unihost"},"image":{"@id":"https:\/\/unihost.com\/help\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/unihost\/","https:\/\/x.com\/unihost","https:\/\/www.instagram.com\/unihost\/?hl=en","https:\/\/www.linkedin.com\/company\/unihost-com","https:\/\/www.youtube.com\/channel\/UCITKsxMDnslQY8brN3advgw"]},{"@type":"Person","@id":"https:\/\/unihost.com\/help\/#\/schema\/person\/bb5ae95f38577c920e6a7507888b715a","name":"Unihost Support","image":{"@type":"ImageObject","inLanguage":"en","@id":"https:\/\/unihost.com\/help\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/a0c9db17c2a0d93e8a0d5ac123f8c5db750ad4d3d5657369c0c4e480f5af77b8?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/a0c9db17c2a0d93e8a0d5ac123f8c5db750ad4d3d5657369c0c4e480f5af77b8?s=96&d=mm&r=g","caption":"Unihost Support"},"sameAs":["https:\/\/unihost.com\/"],"url":"https:\/\/unihost.com\/help\/author\/support\/"}]}},"_links":{"self":[{"href":"https:\/\/unihost.com\/help\/wp-json\/wp\/v2\/posts\/20129","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/unihost.com\/help\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/unihost.com\/help\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/unihost.com\/help\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/unihost.com\/help\/wp-json\/wp\/v2\/comments?post=20129"}],"version-history":[{"count":2,"href":"https:\/\/unihost.com\/help\/wp-json\/wp\/v2\/posts\/20129\/revisions"}],"predecessor-version":[{"id":20131,"href":"https:\/\/unihost.com\/help\/wp-json\/wp\/v2\/posts\/20129\/revisions\/20131"}],"wp:attachment":[{"href":"https:\/\/unihost.com\/help\/wp-json\/wp\/v2\/media?parent=20129"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/unihost.com\/help\/wp-json\/wp\/v2\/categories?post=20129"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/unihost.com\/help\/wp-json\/wp\/v2\/tags?post=20129"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}