Nextcloud-aio with traefik gives 404 on public domain

Support intro

Sorry to hear you’re facing problems. :slightly_frowning_face:

The community help forum (help.nextcloud.com) is for home and non-enterprise users. Support is provided by other community members on a best effort / “as available” basis. All of those responding are volunteering their time to help you.

If you’re using Nextcloud in a business/critical setting, paid and SLA-based support services can be accessed via portal.nextcloud.com where Nextcloud engineers can help ensure your business keeps running smoothly.

Getting help

In order to help you as efficiently (and quickly!) as possible, please fill in as much of the below requested information as you can.

Before clicking submit: Please check if your query is already addressed via the following resources:

(Utilizing these existing resources is typically faster. It also helps reduce the load on our generous volunteers while elevating the signal to noise ratio of the forums otherwise arising from the same queries being posted repeatedly).

Some or all of the below information will be requested if it isn’t supplied; for fastest response please provide as much as you can. :heart:

The Basics

  • Nextcloud Server version (e.g., 29.x.x):
    • Nextcloud AIO v13.5.0
  • Operating system and version (e.g., Ubuntu 24.04):
    • Debian GNU/Linux 12 (bookworm)
  • Web server and version (e.g, Apache 2.4.25):
    • N/A
  • Reverse proxy and version _(e.g. nginx 1.27.2)
    • traefik:v3.6
  • PHP version (e.g, 8.3):
    • not sure
  • Is this the first time you’ve seen this error? (Yes / No):
    • No
  • When did this problem seem to first start?
    • Whenever I stop apache and start traefik
  • Installation method (e.g. AlO, NCP, Bare Metal/Archive, etc.)
    • AIO
  • Are you using CloudfIare, mod_security, or similar? (Yes / No)
    • No

Summary of the issue you are facing:

I have been running Nextcloud on my self-hosted server using Apache for years, and I recently switched to AIO, following instructions for docker compose. Early this week, I found instructions on how to replace Apache proxying by Traefik, but none of the instructions I found worked out of the box. Following traefik-best-practice/docker-traefik-dashboard-letsencrypt/docker-compose.yml at main · bluepuma77/traefik-best-practice · GitHub, I and managed to create a docker-compose.yml that imports ssl certificates from letsencrypt and runs the test whomi image on my public domain. Now, removing the whoami image and following instructions at all-in-one/reverse-proxy.md at main · nextcloud/all-in-one · GitHub , I tried to run my original Nextcloud AIO along with a modified Traefik configuration, but every time I want to access my public domain, I only get a “404 page not found”, whereas I can access the configuration site through localhost:8080.

Can anyone spot the problem in my config? I have tried all three options mentioned wrt the ip and network setting for a proxy server running on the same server in a Docker container, the below is the third one, none of them worked. The same compose.yml works if I switch to Apache proxy, so I assume that I missed something in my traefik setup. Thanks for any hints I can get!

Traefik docker-compose.yml:

services:
  traefik:
    image: traefik:v3.6
    ports:
      - 80:80
      - 443:443
    networks:
      #- proxy
      - nextcloud-aio
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - "./letsencrypt:/letsencrypt"
      - "./conf:/conf"
      - ./log:/log
    command:
      - --api.dashboard=true
      - --log.level=DEBUG
      - --log.filepath=/log/traefik.log
      - --accesslog=true
      - --accesslog.filepath=/log/traefik-access.log
      #- --providers.docker.network=proxy
      - --providers.docker.network=nextcloud-aio
      - --providers.docker.exposedByDefault=false
      - --providers.file.directory=conf
      - --providers.file.watch=true
      - --entrypoints.web.address=:80
      - --entrypoints.web.http.redirections.entrypoint.to=websecure
      - --entryPoints.web.http.redirections.entrypoint.scheme=https
      - --entrypoints.websecure.address=:443
      - --entrypoints.websecure.asDefault=true 
      # https://github.com/nextcloud/all-in-one/blob/main/reverse-proxy.md
      # replaced https by websecure and http by web
      #- --entrypoints.https.address=443
      - --entrypoints.websecure.transport.respondingTimeouts.readTimeout=3610s
      - --entrypoints.websecure.http.encodedCharacters.allowEncodedSlash=true
      - --entrypoints.websecure.http.encodedCharacters.allowEncodedQuestionMark=true
      - --entrypoints.websecure.http.encodedCharacters.allowEncodedPercent=true
      - --entrypoints.websecure.http.tls.certresolver=le
      # Let's Encrypt configuration
      - "--certificatesresolvers.le.acme.email=mymail@somewhere.com" # replace with your actual email
      - "--certificatesresolvers.le.acme.storage=/letsencrypt/acme.json"
      - "--certificatesresolvers.le.acme.httpchallenge.entrypoint=web"
      - "--certificatesresolvers.le.acme.tlschallenge=true"

    labels:
      - traefik.enable=true
      - traefik.http.routers.mydashboard.rule=Host(`traefik.mydomain.eu`)
      - traefik.http.routers.mydashboard.service=api@internal
      - traefik.http.routers.mydashboard.middlewares=myauth
      - traefik.http.middlewares.myauth.basicauth.users=name:$$hash
      
networks:
  nextcloud-aio:
    name: nextcloud-aio
    external: true

volumes:
  letsencrypt:
    name: letsencrypt
  conf:
    name: conf

Here is my .conf/nextcloud.yml:

# https://github.com/nextcloud/all-in-one/blob/main/reverse-proxy.md#adapting-the-sample-web-server-configurations-below
web:
  routers:
    nextcloud:
      rule: "Host(`www.mydomain.eu`)"
      entrypoints:
        - "https"
      service: nextcloud
      middlewares:
        - nextcloud-chain
      tls:
        certresolver: "le"

  services:
    nextcloud:
      loadBalancer:
        servers:
          - url: "http://nextcloud-aio-apache.nextcloud-aio:11000" # Adjust to match APACHE_PORT and APACHE_IP_BINDING. See https://github.com/nextcloud/all-in-one/blob/main/reverse-proxy.md#adapting-the-sample-web-server-configurations-below

  middlewares:
    nextcloud-secure-headers:
      headers:
        hostsProxyHeaders:
          - "X-Forwarded-Host"
        referrerPolicy: "same-origin"
        customRequestHeaders:
          X-Forwarded-Proto: "https"

    https-redirect:
      redirectscheme:
        scheme: https 

    nextcloud-chain:
      chain:
        middlewares:
          # - ... (e.g. rate limiting middleware)
          - https-redirect
          - nextcloud-secure-headers

And finally, in a separate folder, my Nextcloud compose.yml, put together from all-in-one/reverse-proxy.md at main · nextcloud/all-in-one · GitHub and all-in-one/compose.yaml at main · nextcloud/all-in-one · GitHub

name: nextcloud-aio # Add the container to the same compose project to which all the sibling containers are added automatically
services:
  nextcloud-aio-mastercontainer:
    image: ghcr.io/nextcloud-releases/all-in-one:latest # This is the container image used. You can switch to ghcr.io/nextcloud-releases/all-in-one:beta if you want to help testing new releases. See https://github.com/nextcloud/all-in-one#how-to-switch-the-channel
    init: true # This setting makes sure that signals from main process inside the container are correctly forwarded to children. See https://docs.docker.com/reference/compose-file/services/#init
    restart: always # This makes sure that the container starts always together with the host OS. See https://docs.docker.com/reference/compose-file/services/#restart
    container_name: nextcloud-aio-mastercontainer # This line is not allowed to be changed as otherwise AIO will not work correctly
    volumes:
      - nextcloud_aio_mastercontainer:/mnt/docker-aio-config # This line is not allowed to be changed as otherwise the built-in backup solution will not work
      - /var/run/docker.sock:/var/run/docker.sock:ro # May be changed on macOS, Windows or docker rootless. See the applicable documentation. If adjusting, don't forget to also set 'WATCHTOWER_DOCKER_SOCKET_PATH'!
    network_mode: bridge # This adds the container to the same network as docker run would do. Comment this line and uncomment the line below and the networks section at the end of the file if you want to define a custom MTU size for the docker network
#    networks: ["nextcloud-aio"]
    ports:
#      - "80:80" # Can be removed when running behind a web server or reverse proxy (like Apache, Nginx, Caddy, Cloudflare Tunnel and else). See https://github.com/nextcloud/all-in-one/blob/main/reverse-proxy.md
      - "8080:8080" # This is the AIO interface, served via https and self-signed certificate. See https://github.com/nextcloud/all-in-one#explanation-of-used-ports
#     - "8443:8443" # Can be removed when running behind a web server or reverse proxy (like Apache, Nginx, Caddy, Cloudflare Tunnel and else). See https://github.com/nextcloud/all-in-one/blob/main/reverse-proxy.md
    # security_opt: ["label:disable"] # Needed when using SELinux. See https://github.com/nextcloud/all-in-one#are-there-known-problems-when-selinux-is-enabled
    environment: # This line is needed (has to be uncommented) when using any of the options below
      # AIO_DISABLE_BACKUP_SECTION: false # Setting this to true allows to hide the backup section in the AIO interface. See https://github.com/nextcloud/all-in-one#how-to-disable-the-backup-section
      APACHE_PORT: 11000 # Needed when running behind a web server or reverse proxy (like Apache, Nginx, Caddy, Cloudflare Tunnel and else). See https://github.com/nextcloud/all-in-one/blob/main/reverse-proxy.md
      APACHE_IP_BINDING: 0.0.0.0 # Should be set when running behind a web server or reverse proxy (like Apache, Nginx, Caddy, Cloudflare Tunnel and else) that is running on the same host. See https://github.com/nextcloud/all-in-one/blob/main/reverse-proxy.md
#      APACHE_ADDITIONAL_NETWORK: proxy # (Optional) Connect the apache container to an additional docker network. Needed when behind a web server or reverse proxy (like Apache, Nginx, Caddy, Cloudflare Tunnel and else) running in a different docker network on same server. See https://github.com/nextcloud/all-in-one/blob/main/reverse-proxy.md

      NEXTCLOUD_DATADIR: /home/user/nextcloud_aio_data # Allows to set the host directory for Nextcloud's datadir. ⚠️⚠️⚠️ Warning: do not set or adjust this value after the initial Nextcloud installation is done! See https://github.com/nextcloud/all-in-one#how-to-change-the-default-location-of-nextclouds-datadir

volumes: # If you want to store the data on a different drive, see https://github.com/nextcloud/all-in-one#how-to-store-the-filesinstallation-on-a-separate-drive
  nextcloud_aio_mastercontainer:
    name: nextcloud_aio_mastercontainer # This line is not allowed to be changed as otherwise the built-in backup solution will not work

# # Adjust the MTU size of the docker network. See https://github.com/nextcloud/all-in-one#how-to-adjust-the-mtu-size-of-the-docker-network
# networks:
#   nextcloud-aio:
#     name: nextcloud-aio
#     driver_opts:
#       com.docker.network.driver.mtu: 1440
  proxy:
    name: proxy
    external: true

There are some other examples where people ran into similar problems, e.g. https://help.nextcloud.com/t/nextcloud-aio-and-traefik-3-setup-help-needed, but they did not use the current examples from the documentation, and they usually don’t provide their traefik static config, so I hope that someone can help me figure out the problem here and then produce an updated how-to and/or update the documentation.

I am collecting trouble-shooting steps here: https://help.nextcloud.com/t/how-to-troubleshoot-aio-behind-traefik-issues

Hello @schymans!

EDIT: I removed my post, as I should have fact checked the recommendations in AiO repo first. Sorry about that! Please see my comment below :slight_smile:

Oh before you jump ahead on that path, I found another thing that I think might be the issue. Your static traefik config defines two entrypoints; web and websecure

In nextcloud, you are using https - which does not exist - can you try to set that to websecure instead?

Thanks a lot for picking that up, but I am still getting 404. This is where I replaced https by websecure in nextcloud.yml:

web:
  routers:
    nextcloud:
      rule: "Host(`www.mydomain.com`)"
      entrypoints:
        - "websecure"
  middlewares:
    nextcloud-secure-headers:
      headers:
        hostsProxyHeaders:
          - "X-Forwarded-Host"
        referrerPolicy: "same-origin"
        customRequestHeaders:
          X-Forwarded-Proto: "websecure"

    https-redirect:
      redirectscheme:
        scheme: websecure

I then stopped and removed the traefik container and restarted it, but I am still getting 404.

Sorry, I am very new to traefik, and quite new to docker and nextcloud-aio, so I don’t fully understand what I’m doing.

Should I have restarted the nextcloud-aio-apache and/or nextcloud-aio-mastercontainer as well?

I wondered about the proxy network being “bridge” as well, it might be that it was wrongly configured when I created it the first time while trying out traefik. Could you re-share that part again?

Sorry, the ‘proxy’ network is not relevant here, as I am now using the third approach, where traefik connects to the ‘nextcloud-aio’ network rather than nextcloud connecting to the ‘proxy’ network.

In essence - traefik absolutely needs to be able to reach the destination container, otherwise it cannot route traffic to it - and that is most likely why you see the 404.

I think the fix is a pure bridge setup (so no host networking)
We can tell AiO to attach its spawned Apache container directly to a docker network.

1: fix your compose file for AiO

Keep network_mode: bridge on the mastercontainer. Under the environment: section, use these env vars:

    environment:
      APACHE_PORT: 11000
      APACHE_ADDITIONAL_NETWORK: proxy
      SKIP_DOMAIN_VALIDATION: true     # Prevents AiO domain checks from failing behind Traefik

Explanation: These environment variables will be read by nextcloud AiO when the setup is done and it is spinning up the new containers for you. The Apache container sees it, and will use those values for itself.

Next up, enable the “networks” block. We want to reference this network named proxy so we can connect it to traefik.

networks:
  proxy:
    name: proxy
    external: true   # create the network manually using "docker network create proxy"

and then create the network if it does not already exist; run this in your terminal:

docker network create proxy

Now you have:

  1. You define the network via your networks block (you make it available)
  2. Used environment variables for apache to tell it that you want it to listen on port 11000 when it gets created, and have it listen to a specific network named proxy

What remains now, is to tell traefik where it can find this container..

2: Update your Traefik configuration

Since the nextcloud-aio-apache container will now join the network, Traefik can route traffic directly using Docker’s internal DNS.

Change your service target URL in Traefik to:

http://nextcloud-aio-apache:11000

When two containers are on the same network, docker will help resolve the name of the container into an IP - the IP can change, but the name of the container is the same.

3: Update Traefik Compose file:

In traefik docker compose file, you need to un-comment the proxy under networks:, and comment away the nextcloud-aio (disable it).
It should look like this:

services:
   traefik:
# [....]
    networks:
      - proxy
#     - nextcloud-aio

Then, at the bottom, delete your entire networks: block (in the root level). Instead, replace it with this:

networks:
  proxy:
    name: proxy
    external: true  #because we create it manually with "docker network create proxy"

4: apply it all

In order for the configs to bite, you will need to tear down both traefik and the aio stack.

  1. Make sure you created the proxy network: docker network create proxy
  2. run docker compose down && docker compose up -d in your AiO docker-compose folder.
  3. run docker compose down && docker compose up -d in your traefik docker-compose folder too.
  4. Go to https://<your ip>:8080, accept risk with self signed certificate, and follow instructions on the AiO setup (if you are setting up a new instance..)
  5. When you click start containers in the final step, wait until it completes, and check what runs with docker ps (shows all running containers).
  6. Traefik will send its traffic to the apache container on port 11000 as we configured
  7. if things are not working, check if the containers are attached to the network: docker network inspect proxy - you should see both traefik and one (or more) nextcloud aio containers there.


:information_source: Note: Remember that running docker compose up only starts the setup interface on port 8080. This is called the master container.
You still need to log into https://<your-ip>:8080 and click “Start containers” (after entering your FQDN) so the master container can create nextcloud-aio-apache container, and attach it to the proxy network, as we defined.

Thank you so much for your time and detailed instructions! I followed them, but unfortunately, I am still getting the 404 after the last step. I didn’t need to do the AiO setup, as I had done that before, but I stopped all containers and re-started them in Step 4. Docker ps confirmed that the following containers are running: aio-apache:latest, aio-nextcloud:latest, aio-imaginary:latest, aio-clamav:latest, aio-redis:latest, aio-postgresql:latest, aio-whiteboard:latest, aio-notify-push:latest, aio-talk:latest, aio-eurooffice:latest, traefik:v3.6, and all-in-one:latest. Only nextcloud-aio-apache and traefik3-traefik-1 are attached to the network “proxy”.

In many other examples, I saw that people included nextcloud as a service in the static traefik configuration (docker-compose.yml), whereas I only mention nextcloud in my dynamic web setup (docker.yml). Could I be missing something in my static config?

On my phone and unable to provide a very detailed response, but yes - the static configuration is the standard way to route traffic to containers in the aio stack. The dynamic configuration needs labels, and its not straightforward to apply labels to individual containers that aio manages

The url i wanted you to change was this one:

Not sure if you did, as this looks like the static configuration file for traefik :slight_smile: and your traefik is configured to read from the “conf” directory.

Maybe try to set it to it’s full path as you’ve defined it in the volume mounts. Find the existing line in your traefik compose file:

and set it to /conf, as I don’t think traefik looks there by default.

Since both Apache and traefik is in the same network, you’ve already gotten a step in the right direction! :clap: :clap:

Now that the two containers are reachable to eachother, I think you’re close. See if the config path help.

Also check traefik logs if it reveals something while you try.

Thanks again! I think this line in the traefik logs confirms that the path to conf is set correctly: add watcher on: conf/nextcloud.yml

But then there are lines like this: Filtering disabled container container=nextcloud-aio-apache for every single aio-container running. Does this mean anything ominous?

Also, could the line DBG ``github.com/traefik/traefik/v3/pkg/tls/tlsmanager.go:386`` > No default certificate, fallback to the internal generated certificate tlsStoreName=default mean that my certificate setup is incomplete?

I tried to set it up to get Letsencrypt certificates for both www.mydomain.eu and traefik.mydomain.eu and it seems to be succeeding all the way up to this line in the traefik log:

2026-08-25T16:31:21Z ERR github.com/traefik/traefik/v3/pkg/provider/acme/provider.go:571 > Unable to obtain ACME certificate for domains error="unable to generate a certificate for the domains [\"traefik.mydomain.eu\"]: acme: error: 400 :: POST :: https://acme-v02.api.letsencrypt.org/acme/new-order :: urn:ietf:params:acme:error:rejectedIdentifier :: Invalid identifiers requested :: Cannot issue for \"\\\"traefik.mydomain.eu\\\"\": Domain name contains an invalid character" ACME CA=https://acme-v02.api.letsencrypt.org/directory acmeCA=https://acme-v02.api.letsencrypt.org/directory domains=["\"traefik.mydomain.eu\""] providerName=le.acme routerName=mydashboard@docker rule=Host(`traefik.mydomain.eu`)

Same for www.mydomain.eu. Despite that, I can reach traefik.mydomain.eu without problems, whereas www.mydomain.eu gives 404.

Interestingly, the traefik dashboard shows Host(traefik.schymanski.eu) in the HTTP routers section, but nothing about nextcloud.

If I stop and start the nextcloud-aio-apache container, I see it in the traefik logs, which say that the container is healthy, but it does not show up in the traefik dashboard.

hehe, the “disabled container” is normal in this case. Since traefik by default uses labels to discover where to route things, it will say that for any container that is not in scope for auto discovery via labels, so you should be fine with that. This is why we use the static file

This error is a nice clue;

Can you paste your entire conf/nextcloud.yml file here? It may look like the URL including the " - which it should not. Besides, seeing the current state of the file will also help understand if anything else misses, as I can only go by what I told you / you told me so far :slight_smile:

Thanks again, here is my updated conf/nextcloud.yml:

# https://github.com/nextcloud/all-in-one/blob/main/reverse-proxy.md#adapting-the-sample-web-server-configurations-below
web:
  routers:
    nextcloud:
      rule: "Host(`www.mydomain.eu`)"
      entrypoints:
        - "websecure"
      service: nextcloud
      middlewares:
        - nextcloud-chain
      tls:
        certresolver: "le"

  services:
    nextcloud:
      loadBalancer:
        servers:
          - url: "http://nextcloud-aio-apache:11000" # Adjust to match APACHE_PORT and APACHE_IP_BINDING. See https://github.com/nextcloud/all-in-one/blob/main/reverse-proxy.md#adapting-the-sample-web-server-configurations-below

  middlewares:
    nextcloud-secure-headers:
      headers:
        hostsProxyHeaders:
          - "X-Forwarded-Host"
        referrerPolicy: "same-origin"
        customRequestHeaders:
          X-Forwarded-Proto: "websecure"

    https-redirect:
      redirectscheme:
        scheme: websecure 

    nextcloud-chain:
      chain:
        middlewares:
          # - ... (e.g. rate limiting middleware)
          - https-redirect
          - nextcloud-secure-headers

I find the quotation marks in various examples very confusing, as they are sometimes around values, sometimes around a whole line, and sometimes none at all.

I think I found it - your top level key is wrong, it should be “http:” as a category for config in traefik. the http section does all with routing and such. In this case, I suspect that the entire configuration got ignored. I have fixed it for you here. I also saw that the protocol forwarding header was incorrect:

  # https://github.com/nextcloud/all-in-one/blob/main/reverse-proxy.md#adapting-the-sample-web-server-configurations-below
  http:  # changed from "web" - Traefik uses "http/tcp/udp/tls for top level keys. Routers and such go under "http" section.
  got loaded, hence the 404)
    routers:
      nextcloud:
        rule: "Host(`www.mydomain.eu`)"
        entrypoints:
          - "websecure"
        service: nextcloud
        middlewares:
          - nextcloud-chain
        tls:
          certresolver: "le"

    services:
      nextcloud:
        loadBalancer:
          servers:
            - url: "http://nextcloud-aio-apache:11000"

    middlewares:
      nextcloud-secure-headers:
        headers:
          hostsProxyHeaders:
            - "X-Forwarded-Host"
          referrerPolicy: "same-origin"
          customRequestHeaders:
            X-Forwarded-Proto: "https"  # changed from "websecure". This is the actual "protocol" you are forwarding, and has nothing to do with traefik.

      https-redirect:
        redirectscheme:
          scheme: websecure   # this is correct because it tells traefik which listener to redirect to. 

      nextcloud-chain:
        chain:
          middlewares:
            - https-redirect
            - nextcloud-secure-headers

Also to answer your question about the quoting - in yaml, all text values are most of the time, treated as strings by default - there are exceptions to that rule (Special characters, numbers). You can see more about that here: YAML Best Practices - YS — YAML Done Wisely

Now I think you should be getting this working, crossing my paws :paw_prints: :smiley:

Just make sure to restart traefik after this config change. It should apply the static config in full.

Thanks a lot for the explanations, I guess I didn’t know what I was doing when replacing http by web and https by websecure.

No I get a different error when trying to reach my domain:


The address wasn’t understood

Firefox doesn’t know how to open this address, because one of the following protocols (https) isn’t associated with any program or is not allowed in this context.

    You might need to install other software to open this address.

I guess that I am still missing a line somewhere.

that one is new to me too :thinking:

can you try curl -vL [your domain] and see if something shows up?

feel free to redact your domain/ips before posting output ^^

Very good idea, thanks! Here is the relevant part of the output:

* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
< HTTP/2 302 
< location: websecure://<mydomain>/
< content-length: 5
< date: Thu, 27 Aug 2026 22:16:23 GMT
< 
* Ignoring the response-body
* Connection #0 to host <mydomain> left intact
* The redirect target URL could not be parsed: Unsupported URL scheme
curl: (1) The redirect target URL could not be parsed: Unsupported URL scheme

So I still have one too many “websecure” instead of “https” in my script. Now that I know what to look for, I’ll do some trial and error…

Hooray, this did it:

https-redirect:
        redirectscheme:
          scheme: "https"
          #scheme: websecure   # this is correct because it tells traefik which listener to redirect to. 

I.e. it had to be https, not websecure here. Now it works! I’ll do some more checks then share the updated setup.

Thanks again for your help! Can I buy you a coffee? :slight_smile:

Whoops, my bad for giving you the wrong config there, but glad that curl could be more verbose about the issue (which I was hoping for). Well done

Very glad it worked out for you! Been a real headscratcher :partying_face: