5 min read

Reverse Proxy on Oracle VPS with Traefik

Reverse Proxy on Oracle VPS with Traefik
1 - How I Built My Own Server Using Local and Cloud Resources
2 - Deploying containers via Cloudflare Tunnel (Zero Trust)
3 - Reverse proxy on Oracle VPS with Traefik
4 - Encrypted Server Backups to S3 with Duplicati

In the previous part, we transformed a set-top box (STB) into a personal Docker server, complete with Portainer and Cloudflare Tunnel.

Now, let’s take it a step further.

In this part, we’ll set up a Reverse Proxy using Traefik on an Oracle Cloud Free Tier VPS (or any VPS). This allows us to publicly expose any containerized app with SSL (HTTPS) using Cloudflare — without relying on a Cloudflare Tunnel.

This setup is perfect for those who want direct access via public IP, fine control over reverse proxy routing, and enhanced flexibility with SSL/TLS cert management.


Requirements

Before we begin, make sure you already have:

  • A Cloudflare account
  • An Oracle VPS (Free Tier) with public IP address, and Docker installed.
If you don’t have one, follow this step-by-step guide: Oracle Free Tier Setup by Tomasz Dunia
  • A registered domain, added to your Cloudflare dashboard
It doesn’t have to be registered through Cloudflare, just ensure the nameservers point to Cloudflare. You can search online for domain registration and setup help.

The Oracle Cloud Free Tier provides generous resources — 4 OCPUs, 24 GB RAM, and 200 GB Storage. You're allowed to deploy multiple instances as long as you stay within these limits. If you exceed them, you will be charged.

Be aware that Oracle may automatically stop or remove your Always Free instances if they appear unused. According to Oracle’s official documentation, if all three of the following are true during a 7-day period, your instance could be reclaimed:

  • CPU utilization (95th percentile) is below 20%
  • Network utilization is below 20%
  • Memory utilization is below 20% (A1 shapes only)

Just make sure you're using your instance actively — even light usage should keep it safe.


Step 1: Point Domain to VPS Public IP

  1. Go to your Cloudflare dashboard
  2. Select your domain
  3. Navigate to the DNS tab
  4. In the "DNS Management" section, add the following two records:

Both should be of type A — one with @ for the root domain (domain.com), and one with * for all subdomains (*.domain.com). Replace your.ip.address with your actual IP address.

Save it, and it should look like this:


Step 2: (Optional) Generate and Save Cloudflare Origin Certificates

In this tutorial, I will use a certificate from Cloudflare, but you can skip this step if you prefer to use Let’s Encrypt instead.

  1. Go to SSL/TLS → Origin Server
  2. Click Create Certificate
  3. Configure it as shown, then click Create
  4. You will be given two certificates:
    • One for the certificate (save it as a .pem file on your server — just copy and paste)
    • One for the private key (save it as a .key file on your server)

Step 3: Configure How Cloudflare Encrypts Traffic

In this step, we’ll ensure that Cloudflare communicates with our server using HTTPS only.

  1. Go to SSL/TLS → Overview.
  2. Set the SSL/TLS encryption mode to Full.

Step 4: Setup Traefik Reverse Proxy

Before we begin, make sure you have opened port 443 (and 80 if you want to) on your Oracle VPS and firewall. If you haven’t done this yet, follow the tutorial in the link provided above, under the “Opening ports” section.

Step 1. Create Docker network

docker network create public-net

Step 2. Create a folder with the following format

traefik
├── certs
│   ├── your-private-key.key
│   └── your-cert.pem
├── docker-compose.yml
├── dynamic
│   └── basicauth.yml
└── traefik.yml

Here’s the example config for each file:

docker-compose.yml

services:
  traefik:
    image: traefik:v3.0
    container_name: traefik
    ports:
      # - "80:80" # uncomment this line if you need 80 port open
      - "443:443"
    volumes:
      - "./traefik.yml:/etc/traefik/traefik.yml:ro"
      - "./certs:/certs:ro" # comment this line if you want to use lets encrypt
      - "./dynamic:/etc/traefik/dynamic:ro"
      # - "./acme.json:/acme.json" # uncomment this line if you want to use lets encrypt
      - "/var/run/docker.sock:/var/run/docker.sock"
    networks:
      - public-net
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.traefik.rule=Host(`traefik.doman.com`)"
      - "traefik.http.routers.traefik.entrypoints=websecure"
      - "traefik.http.routers.traefik.tls=true"
      - "traefik.http.services.traefik.loadbalancer.server.port=8080"
      - "traefik.http.routers.traefik.service=api@internal"
      - "traefik.http.routers.traefik.middlewares=basic-auth@file"
networks:
  public-net:
    external: true

basicauth.yml

http:
  middlewares:
    basic-auth:
      basicAuth:
        users:
          - "<YOUR_USERNAME_AND_PASSWORD>"
    default:
      chain:
        middlewares:
          - default-security-headers
          - gzip

    secHeaders:
      chain:
        middlewares:
          - default-security-headers
          - gzip

    default-security-headers:
      headers:
        browserXssFilter: true
        contentTypeNosniff: true
        forceSTSHeader: true
        frameDeny: true
        stsIncludeSubdomains: true
        stsPreload: true
        stsSeconds: 31536000
        customFrameOptionsValue: "SAMEORIGIN"
    gzip:
      compress: {}
Replace <YOUR_USERNAME_AND_PASSWORD> with the output generated by the following command:
htpasswd -nb your_username your_password

# the output can be something like
your_username:xxxyourhashedpasswordxxx

traefik.yml

entryPoints:
  #web:
  #  address: ":80" # uncomment this section if you want to open 80 port
  websecure:
    address: ":443"
    transport:
      respondingTimeouts:
        readTimeout: 600s
        idleTimeout: 600s
        writeTimeout: 600s
  metrics:
    address: ":9100"

api:
  dashboard: true
  insecure: false

global:
  checknewversion: true
  sendanonymoususage: false

# cloudflare cert, comment stores and certificates section if you want to use lets encrypt
tls:
  stores:
    default:
      defaultCertificate:
        certFile: "/certs/your-cert.pem"
        keyFile: "/certs/your-private-key.key"
  certificates:
    - certFile: /certs/your-cert.pem
      keyFile: /certs/your-private-key.key
  options:
    default:
      minVersion: VersionTLS12
      cipherSuites:
        - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
        - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
        - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305
        - TLS_AES_128_GCM_SHA256
        - TLS_AES_256_GCM_SHA384
        - TLS_CHACHA20_POLY1305_SHA256
      curvePreferences:
        - CurveP521
        - CurveP384
      sniStrict: true

providers:
  docker:
    exposedByDefault: false
  file:
    directory: /etc/traefik/dynamic
    watch: true
  providersThrottleDuration: 10

metrics:
  prometheus:
    addEntryPointsLabels: true
    addServicesLabels: true
    entryPoint: metrics

# letsencrypt setting, uncomment this if you want to use lets encrypt
#certificatesResolvers:
#  myresolver:
#    acme:
#      email: [email protected]
#      storage: acme.json
#      httpChallenge:
#        entryPoint: web

If everything is ready, just run the command inside the traefik folder.

docker compose up -d

Your Traefik reverse proxy should be up and running successfully.


Step 5: Deploy Example App (Nginx)

Now we’re ready to deploy our first app — for example, Nginx.

Create another folder outside the traefik folder, and create a docker-compose.yml file by running:

nano docker-compose.yml 

Then fill it with the following content:

services:
  app:
    image: nginx:latest
    container_name: nginx
    networks:
      - public-net
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.nginx.rule=Host(`mynginx.domain.com`)"
      - "traefik.http.routers.nginx.entrypoints=websecure"
      - "traefik.http.routers.nginx.tls=true"
      - "traefik.http.services.nginx.loadbalancer.server.port=80"
      # uncomment this below if you use lets encrypt
      # - "traefik.http.routers.nginx.tls.certresolver=myresolver"

networks:
  public-net:
    external: true

Now run it using:

docker compose up -d

Your Nginx app should now be accessible at:
https://mynginx.domain.com


Conclusion

Now you have a fully functional reverse proxy with SSL, domain routing, and Dockerized apps — all running on a free VPS from Oracle Cloud.

You can now deploy as many apps as you want, using subdomains with full HTTPS coverage and easy container-based control.