{"id":9213,"date":"2026-11-11T17:41:32","date_gmt":"2026-11-11T12:11:32","guid":{"rendered":"https:\/\/www.veeble.com\/kb\/?p=9213"},"modified":"2026-11-11T17:41:32","modified_gmt":"2026-11-11T12:11:32","slug":"cloudflare-tunnel-setup-connect-your-server","status":"publish","type":"post","link":"https:\/\/www.veeble.com\/kb\/cloudflare-tunnel-setup-connect-your-server\/","title":{"rendered":"Cloudflare Tunnel: Easily Expose Local Apps Without Open Ports"},"content":{"rendered":"\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/www.veeble.com\/kb\/wp-content\/uploads\/2026\/11\/Cloudflare-Tunnel-Setup-Guide.jpg\"><img fetchpriority=\"high\" decoding=\"async\" width=\"1024\" height=\"538\" src=\"https:\/\/www.veeble.com\/kb\/wp-content\/uploads\/2026\/11\/Cloudflare-Tunnel-Setup-Guide-1024x538.png\" alt=\"\" class=\"wp-image-9233\" srcset=\"https:\/\/www.veeble.com\/kb\/wp-content\/uploads\/2026\/11\/Cloudflare-Tunnel-Setup-Guide-1024x538.png 1024w, https:\/\/www.veeble.com\/kb\/wp-content\/uploads\/2026\/11\/Cloudflare-Tunnel-Setup-Guide-300x158.png 300w, https:\/\/www.veeble.com\/kb\/wp-content\/uploads\/2026\/11\/Cloudflare-Tunnel-Setup-Guide-768x403.png 768w, https:\/\/www.veeble.com\/kb\/wp-content\/uploads\/2026\/11\/Cloudflare-Tunnel-Setup-Guide-1536x806.png 1536w, https:\/\/www.veeble.com\/kb\/wp-content\/uploads\/2026\/11\/Cloudflare-Tunnel-Setup-Guide-2048x1075.png 2048w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n\n<h2 class=\"wp-block-heading\" id=\"introduction\">Introduction<\/h2>\n\n\n<p class=\"wp-block-paragraph\">Cloudflare Tunnel can be used to connect a server or locally hosted application to <strong><a href=\"https:\/\/www.cloudflare.com\/\" target=\"_blank\" rel=\"noopener\">Cloudflare<\/a><\/strong> without having to expose the origin server&#8217;s IP address or open inbound ports for the application.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In this guide, I will go through the process of setting up a locally managed tunnel using <code><a href=\"https:\/\/www.cloudflare.com\/\" data-type=\"link\" data-id=\"https:\/\/www.cloudflare.com\/\" target=\"_blank\" rel=\"noopener\"><strong>cloudflared<\/strong><\/a><\/code> from the command line. We will install <code><strong><a href=\"https:\/\/www.cloudflare.com\/\" target=\"_blank\" rel=\"noopener\">cloudflared<\/a><\/strong><\/code> on the server, authenticate it with Cloudflare, create a tunnel, configure the required DNS record, and connect the tunnel to a locally running application. We will also configure the tunnel to run as a system service so that it continues running in the background.<\/p>\n\n\n<h2 class=\"wp-block-heading\" id=\"prerequisites\">Prerequisites<\/h2>\n\n\n<p class=\"wp-block-paragraph\">Before starting, make sure the domain is already added to Cloudflare and its nameservers are pointing to Cloudflare. You will also need terminal access to the server where you intend to configure the tunnel.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For this example, we will use <code>app.example.com<\/code> as the hostname and <code>http:\/\/localhost:8000<\/code> as the application running on the server. You can replace these values with your actual domain and application details. If the server is behind a restrictive firewall, make sure it can establish outbound connections to Cloudflare on port <code>7844<\/code> using the required transport protocol.<\/p>\n\n\n<h2 class=\"wp-block-heading\" id=\"step-1-install-cloudflared\">Step 1: Install cloudflared<\/h2>\n\n\n<p class=\"wp-block-paragraph\">The first step is to install <code>cloudflared<\/code> on the server. The installation method depends on the operating system you are using.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">On Ubuntu or Debian, you can install it using <a href=\"https:\/\/pkg.cloudflare.com\/index.html\" data-type=\"link\" data-id=\"https:\/\/pkg.cloudflare.com\/index.html\" target=\"_blank\" rel=\"noopener\">Cloudflare&#8217;s official package repository<\/a>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo mkdir -p --mode=0755 \/usr\/share\/keyrings\n\ncurl -fsSL https:\/\/pkg.cloudflare.com\/cloudflare-main.gpg \\\n  | sudo tee \/usr\/share\/keyrings\/cloudflare-main.gpg &gt;\/dev\/null\n\necho \"deb &#091;signed-by=\/usr\/share\/keyrings\/cloudflare-main.gpg] https:\/\/pkg.cloudflare.com\/cloudflared any main\" \\\n  | sudo tee \/etc\/apt\/sources.list.d\/cloudflared.list\n\nsudo apt-get update\nsudo apt-get install cloudflared<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">For AlmaLinux or Rocky Linux, add the Cloudflare repository and install the package using DNF:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo curl -fsSL https:\/\/pkg.cloudflare.com\/cloudflared.repo \\\n  -o \/etc\/yum.repos.d\/cloudflared.repo\n\nsudo dnf install cloudflared<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Once the installation is complete, check whether <code>cloudflared<\/code> is installed correctly by running:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cloudflared --version<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The command should return the installed version. If it does, we can proceed with the tunnel configuration.<\/p>\n\n\n<h2 class=\"wp-block-heading\" id=\"step-2-authenticate-with-cloudflare\">Step 2: Authenticate with Cloudflare<\/h2>\n\n\n<p class=\"wp-block-paragraph\">Before creating the tunnel, we need to authenticate <code>cloudflared<\/code> with the Cloudflare account that manages the domain.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Run the following command:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cloudflared tunnel login<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The command will provide a URL to open in a browser. Log in to your Cloudflare account, select the domain you want to use, and authorize the connection.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Once the authentication is completed, a file named <code><strong>cert.pem<\/strong><\/code> will be generated in the default <code><strong>.cloudflared<\/strong><\/code> directory. If you are running the command as root, the file will normally be available under <code><strong>\/root\/.cloudflared\/<\/strong><\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This certificate is required for managing locally configured tunnels, so make sure it remains accessible to the appropriate user. Do not share the certificate publicly.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If you are configuring the tunnel on a remote server without a browser, you can open the authentication URL from your local computer and complete the process there.<\/p>\n\n\n<h2 class=\"wp-block-heading\" id=\"step-3-create-the-tunnel\">Step 3: Create the tunnel<\/h2>\n\n\n<p class=\"wp-block-paragraph\">After authentication, we can create the tunnel by running:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cloudflared tunnel create test-tunnel<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">You can replace <code>test-tunnel<\/code> with any suitable name for your tunnel.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Once the command completes, Cloudflare will create the tunnel and assign it a unique UUID. A credentials file will also be generated in the <code><strong>.cloudflared<\/strong><\/code> directory. This file is required when starting the tunnel.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">To confirm that the tunnel has been created, run:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cloudflared tunnel list<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">You should see the newly created tunnel in the output. Keep the tunnel UUID and the path to the credentials file handy, as we will need them in the next step.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">At this stage, the tunnel has been created, but it is not yet routing traffic to the application.<\/p>\n\n\n<h2 class=\"wp-block-heading\" id=\"step-4-configure-the-tunnel\">Step 4: Configure the tunnel<\/h2>\n\n\n<p class=\"wp-block-paragraph\">Now that the tunnel is created, we need to configure it to forward requests from the public hostname to the application running on the server.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For this example, we will use <code><strong>\/root\/.cloudflared\/config.yml<\/strong><\/code> as the configuration file. Create the file using a text editor:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>nano \/root\/.cloudflared\/config.yml<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Add the following configuration:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>tunnel: &lt;TUNNEL_UUID&gt;\ncredentials-file: \/root\/.cloudflared\/&lt;TUNNEL_UUID&gt;.json\n\ningress:\n  - hostname: app.example.com\n    service: http:\/\/localhost:8000\n\n  - service: http_status:404<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Replace <code>&lt;TUNNEL_UUID&gt;<\/code> with the UUID generated when creating the tunnel. Also, replace <code>app.example.com<\/code> and <code>localhost:8000<\/code> with the hostname and application URL you intend to use.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Here, we are instructing <code>cloudflared<\/code> to forward requests for <code>app.example.com<\/code> to the application listening on port 8000. The final rule returns a 404 response for requests that do not match the configured hostname.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Save the file once the changes are complete.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Before starting the tunnel, please validate the configuration:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cloudflared tunnel ingress validate<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If the validation completes successfully, we can proceed to configure the DNS record.<\/p>\n\n\n<h2 class=\"wp-block-heading\" id=\"step-5-configure-the-dns-record\">Step 5: Configure the DNS record<\/h2>\n\n\n<p class=\"wp-block-paragraph\">The next step is to connect the hostname to the tunnel. Run the following command:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cloudflared tunnel route dns test-tunnel app.example.com<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Replace the tunnel name and hostname with the values you configured earlier.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This command creates a CNAME record in the Cloudflare DNS zone, pointing the hostname to the tunnel&#8217;s assigned <code>cfargotunnel.com<\/code> address.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Once the command completes, log in to the Cloudflare dashboard, open the relevant domain, and check the DNS records to confirm that the CNAME record has been created correctly.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If a record for the same hostname already exists, check the existing configuration before making any changes.<\/p>\n\n\n<h2 class=\"wp-block-heading\" id=\"step-6-start-the-tunnel-and-test-the-connection\">Step 6: Start the tunnel and test the connection<\/h2>\n\n\n<p class=\"wp-block-paragraph\">Before starting the tunnel, confirm that the application is running and accessible locally.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For our example, run:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>curl -I http:\/\/localhost:8000<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If the application responds, start the tunnel using:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cloudflared tunnel run test-tunnel<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">You should see the connection logs in the terminal as <code>cloudflared<\/code> establishes its connections to Cloudflare.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Keep this session running while testing the setup. If you close the terminal or stop the process, the tunnel instance will stop.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Now open <code>https:\/\/app.example.com<\/code> in a browser or test it using:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>curl -I https:\/\/app.example.com<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If everything is configured correctly, the request should reach the application through Cloudflare.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If the hostname does not load, check the tunnel logs, DNS record, and local application before making further changes. A tunnel can be connected successfully while the application itself is still inaccessible.<\/p>\n\n\n<h2 class=\"wp-block-heading\" id=\"step-7-run-the-tunnel-as-a-service\">Step 7: Run the tunnel as a service<\/h2>\n\n\n<p class=\"wp-block-paragraph\">Once we have confirmed that the tunnel is working, we can configure it to run as a system service instead of keeping it active in a terminal session.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">First, stop the manually running tunnel using <code>Ctrl+C<\/code>. Then install the service:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo cloudflared service install<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This command uses the configuration discovered by <code>cloudflared<\/code> for the installation context. If your configuration is stored in a custom location or under a different user&#8217;s home directory, make sure the service is configured to use the correct file and has access to the credentials.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Start the service and enable it to run automatically after a server reboot:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo systemctl enable --now cloudflared<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">To check whether the service is running, use:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo systemctl status cloudflared<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If the service starts successfully, the tunnel should continue running in the background.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">To check the logs if the tunnel is not working as expected, run:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo journalctl -u cloudflared -n 50 --no-pager<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If you make changes to the configuration file later, restart the service to apply them:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo systemctl restart cloudflared<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">After restarting, check the service status and test the hostname again to make sure the tunnel is working as expected.<\/p>\n\n\n<h2 class=\"wp-block-heading\" id=\"troubleshooting\">Troubleshooting<\/h2>\n\n\n<p class=\"wp-block-paragraph\">If the tunnel fails to start, the first things I would check are the tunnel UUID, credentials file path, and configuration file. An incorrect UUID or an inaccessible credentials file can prevent <code>cloudflared<\/code> from starting properly.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If the tunnel starts but the website returns a 502 Bad Gateway error, check whether the application is running and listening on the configured port. You can test this directly from the server using <code>curl<\/code> against the local application URL.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If the hostname returns a 404 error, check whether the hostname in the request matches the ingress rule in <code>config.yml<\/code>. Also confirm that the DNS record points to the correct tunnel.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If the tunnel cannot establish a connection to Cloudflare, check the server&#8217;s outbound firewall rules and verify that the required connectivity on port 7844 is permitted.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If the tunnel works when started manually but fails after a reboot, check the <code>cloudflared<\/code> system service and review its logs. Make sure the service is enabled and can access the configuration and credentials files.<\/p>\n\n\n<h2 class=\"wp-block-heading\" id=\"conclusion\">Conclusion<\/h2>\n\n\n<p class=\"wp-block-paragraph\">Once the tunnel is created, the configuration is in place, and the DNS record is pointing to the correct tunnel, the application should be accessible through the configured Cloudflare hostname.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I recommend testing the tunnel manually first and configuring it as a system service only after confirming that the application is reachable through the public hostname. This makes it easier to identify whether an issue is related to the application, DNS configuration, tunnel connectivity, or system service.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Introduction Cloudflare Tunnel can be used to connect a server or locally hosted application to Cloudflare without having to expose the origin server&#8217;s IP [&hellip;]<\/p>\n","protected":false},"author":14,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_uag_custom_page_level_css":"","site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"set","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"categories":[15,3,1],"tags":[],"class_list":["post-9213","post","type-post","status-publish","format-standard","hentry","category-developers-stuff","category-installation","category-uncategorized"],"uagb_featured_image_src":{"full":false,"thumbnail":false,"medium":false,"medium_large":false,"large":false,"1536x1536":false,"2048x2048":false},"uagb_author_info":{"display_name":"Jeevan Kurian","author_link":"https:\/\/www.veeble.com\/kb\/author\/jeevan\/"},"uagb_comment_info":0,"uagb_excerpt":"Introduction Cloudflare Tunnel can be used to connect a server or locally hosted application to Cloudflare without having to expose the origin server&#8217;s IP [&hellip;]","_links":{"self":[{"href":"https:\/\/www.veeble.com\/kb\/wp-json\/wp\/v2\/posts\/9213","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.veeble.com\/kb\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.veeble.com\/kb\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.veeble.com\/kb\/wp-json\/wp\/v2\/users\/14"}],"replies":[{"embeddable":true,"href":"https:\/\/www.veeble.com\/kb\/wp-json\/wp\/v2\/comments?post=9213"}],"version-history":[{"count":3,"href":"https:\/\/www.veeble.com\/kb\/wp-json\/wp\/v2\/posts\/9213\/revisions"}],"predecessor-version":[{"id":9235,"href":"https:\/\/www.veeble.com\/kb\/wp-json\/wp\/v2\/posts\/9213\/revisions\/9235"}],"wp:attachment":[{"href":"https:\/\/www.veeble.com\/kb\/wp-json\/wp\/v2\/media?parent=9213"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.veeble.com\/kb\/wp-json\/wp\/v2\/categories?post=9213"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.veeble.com\/kb\/wp-json\/wp\/v2\/tags?post=9213"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}