Move nextcloud to sub url but put a public folder at root (with nextcloud + nginx + nginx-docker-gen all in docker)

I have nextcloud running on https://cloud.museumsstrasse.at but since we will use nextcloud primarily for opening data to the public I would like that this nextcloud root url leads to a public nextcloud folder directly, e.g. to use this one: https://cloud.museumsstrasse.at/s/Ytpcwos3o9o33kL but so that the public would not need the url with the share sub-url but only type in https://cloud.museumsstrasse.at where this public folder would be opened.
As for logging in and synchronizing with nextcloud I would then like to use another sub url such as this for example: https://cloud.museumsstrasse.at/internal

Is this possible and how?

My setup

I got nextcloud running with docker on a Ubuntu 20.04 Server. Since other services are running on this server too, I set up a reverse proxy solution all with docker too including these images: nginx, jwilder/docker-gen:0.7.3, jrcs/letsencrypt-nginx-proxy-companion which do the reverse proxy and also generate the config files for reverse proxy. The docker-compose.yml for all this is this:

version: '3'

services:

  service_nginx:
    image: nginx:1.13.1
    container_name: container_nginx
    ports:
      - "80:80"
      - "443:443"
    networks:
      - network_nginx
    volumes:
      - ./volumes/nginx/conf:/etc/nginx/conf.d
      - ./volumes/nginx/vhost:/etc/nginx/vhost.d
      - ./volumes/nginx/html:/usr/share/nginx/html
      - ./volumes/nginx-letsencrypt/certs:/etc/nginx/certs
    labels:
      - "com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy=true"

  service_nginx-dockergen:
    image: jwilder/docker-gen:0.7.3
    container_name: container_nginx-dockergen
    depends_on:
      - service_nginx
    command: -notify-sighup container_nginx -watch -wait 5s:30s /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf
    networks:
      - network_nginx
    volumes:
      - ./volumes/nginx/conf:/etc/nginx/conf.d
      - ./volumes/nginx/vhost:/etc/nginx/vhost.d
      - ./volumes/nginx/html:/usr/share/nginx/html
      - ./volumes/nginx/nginx.tmpl:/etc/docker-gen/templates/nginx.tmpl:ro
      - ./volumes/nginx-letsencrypt/certs:/etc/nginx/certs
      - /var/run/docker.sock:/tmp/docker.sock:ro

  service_nginx-letsencrypt:
    image: jrcs/letsencrypt-nginx-proxy-companion
    container_name: container_nginx-letsencrypt
    depends_on:
      - service_nginx
      - service_nginx-dockergen
    environment:
      NGINX_PROXY_CONTAINER: container_nginx
      NGINX_DOCKER_GEN_CONTAINER: container_nginx-dockergen
    networks:
      - network_nginx
    volumes:
      - ./volumes/nginx/conf:/etc/nginx/conf.d
      - ./volumes/nginx/vhost:/etc/nginx/vhost.d
      - ./volumes/nginx/html:/usr/share/nginx/html
      - ./volumes/nginx-letsencrypt/certs:/etc/nginx/certs
      - /var/run/docker.sock:/var/run/docker.sock:ro

  service_nc_db:
    image: mariadb
    container_name: container_nc_db
    volumes:
      - ./volumes/nc_db:/var/lib/mysql
      - /etc/localtime:/etc/localtime:ro
    networks:
      - network_nc_prod
    environment:
      - MYSQL_ROOT_PASSWORD=XXX
      - MYSQL_PASSWORD=XXX
      - MYSQL_DATABASE=XXX
      - MYSQL_USER=XXX
    restart: unless-stopped

  service_nc:
    image: nextcloud:latest
    container_name: container_nc
    depends_on:
      - service_nc_db
      - service_nginx
      - service_nginx-dockergen
      - service_nginx-letsencrypt
    networks:
      - network_nc_prod
      - network_nginx
    volumes:
      - ./volumes/nc/html:/var/www/html
      - ./volumes/nc/config:/var/www/html/config
      - ./volumes/nc/custom_apps:/var/www/html/custom_apps
      - ./volumes/nc/data:/var/www/html/data
      - ./volumes/nc/themes:/var/www/html/themes
      - /etc/localtime:/etc/localtime:ro
    environment:
      - VIRTUAL_HOST=cloud.museumsstrasse.at
      - LETSENCRYPT_HOST=cloud.museumsstrasse.at
      - LETSENCRYPT_EMAIL=XXX
    restart: unless-stopped

networks:
  network_ca_prod:
  network_nc_prod:
  network_nginx:

What I have tried so far is pushing the html code of nextcloud into a subfolder and fiddling around with settings such as overwritewebroot or overwritehost in config.php and various experimentation on nginx reverse proxying, but it all failed so far.
What I have tried to follow were discussions such as these for example:



But I could not get it working because whenever I restart the container, everything within the code in /var/www/html is deleted and reset.

Can someone help me here please?

Cheers,
Stefan

Sounds like you modified the files inside the Docker container and not mapped to a mount outside of the container.

Pretty sure not since the relevant folders are mounted as defined here:

  service_nc:
    image: nextcloud:latest
...
    volumes:
      - ./volumes/nc/html:/var/www/html
      - ./volumes/nc/config:/var/www/html/config
      - ./volumes/nc/custom_apps:/var/www/html/custom_apps
      - ./volumes/nc/data:/var/www/html/data
      - ./volumes/nc/themes:/var/www/html/themes
      - /etc/localtime:/etc/localtime:ro

I tried moving the code in ./volumes/nc/html to ./volumes/nc/html/internal but when starting the container everything in ./volumes/nc/html on the host and in /var/www/html within the container gets overwritten with some default nextcloud code. So it looks like that the nextcloud image checks on container creation time for valid nextcldoud code in /var/www/html and if not found, erases everything there and replaces it with defaults?

Regardless however Iā€™m not sure if this is the desired way anyway, since even if that worked, how would I then manage to have the public folder (which is then a subfolder of /var/www/html/internal) mapped to the root url /var/www/html ?

Never seen a sample with the dots at the beginning of the hosts path.
But if the directories are empty the container adds the default files.

The thing is that the folder is not empty, just that the code is put into the subfolder internal and no changed settings as outlined in my post and in the linked discussions did help to let nextcloud know about this change of code location. And even if it did, Iā€™m sceptical if it would achieve the desired result as I just explained in my previous messageā€¦

For example what doesnā€™t work is if I add to config.php these two lines:

  'overwritehost' => 'cloud.museumsstrasse.at',
  'overwritewebroot' => '/internal'

Then opening cloud.museumsstrasse.at the browser would get redirected to cloud.museumsstrasse.at/internal but in the meantime the code in /var/www/html/internal/<nextcloud_code> got deleted (and hence returns a 404) and default nextcloud code put into /var/www/html/<nextcloud_code> which I then can not open because the browser would again be redirected to cloud.museumsstrasse.at/internal.

However even if something with overwritewebroot were working, how could I avoid this redirection from cloud.museumsstrasse.at to cloud.museumsstrasse.at/internal? Because the former should be the public face and the latter our internal nextcloud side. Hence I want no redirection but two different urls for different purposes.

I assume if a pragmatic way would be to adapt nextcloudā€™s docker image logic to point it to /var/www/html/internal/ and then adapt the this dockerā€™s internal apache to show cloud.museumsstrasse.at while serving content from the public foldercloud.museumsstrasse.at/s/Ytpcwos3o9o33kL ? However Iā€™m at a loss of how to do thisā€¦

I think your idea is not really good. Your idea is stupid. Your idea makes no sense.

There are hundreds of nextcloud programmers and thousands of nextcloud users and nobody needs this feature. It is only a stupid sub-path ā€œinternalā€ and nobody needs it. It is no security feature. It is nothing. With every modification of your nextcloud you get more problems with maintenance and upgrades.

If you really want to differ between private and public i think you must implement two nextclouds (user can use federation between the nextclouds) and in the private nextcloud you do not allow sharing with public shares.

For the private nextcloud you can use https://internal.museumsstrasse.at and for the external nextcloud and https://cloud.museumsstrasse.at for the external nextcloud . Perhaps you can synchronize your user/passwords or using ldap. ā€œinternalā€ uses a different data-dir to ā€œcloudā€. In the internal nextcloud https://internal.museumsstrasse.at you can use external storage with ā€œLocalā€ to the public cloud data but not vice versa.

If you use futher apps use them primary in your internal nextcloud. For sharing with external people you can move the data from the internal to the external nextcloud with https://nextcloud.com/de/federation/

If you do not like it use only one nextcloud https://cloud.museumsstrasse.at and do not change anything. If the user wants to share secret data he/she shares them or send them with e-mail.

Hahaha ā€¦ wow, you must be lovely to be around with.

Due to your language and lack of constructive feedback, I will not reply to your messages any further.

Reconsider your behavior. This is far from professional and by this you contribute to an already high toxicity in the dev community.

Ok. Sorry english ist not my first language. In german i think i can find better words :wink:
But i know a lot of companies who blend software although millions of user use it on the default and mostly right way. The most sofware blend lead to problems with maintenance, upgrades and security.

@steffres you have to configure the nginx router. not nextcloud.

you need two proxy settings. the first redirects all incoming request ending with
ā€œhttps://cloud.museumsstrasse.at/ā€ to ā€œhttp://service_nc/s/Ytpcwos3o9o33kLā€
and the second one
ā€œhttps://cloud.museumsstrasse.at/internalā€ to ā€œhttp://service_nc/ā€

but since the routing through your nginx setup is done by the jwilder/jrcs/letsencrypt container ensemble i canā€™t tell you how. the nginx conf for ā€œcloud.museumsstrasse.atā€ is created automatically by using ā€œVIRTUAL_HOST=cloud.museumsstrasse.atā€

I agree with you that software should be used as close as possible to its intented purpose and that defaults are the way to go. Absolutely. However if customization is possible without interference into a healthy base setup, then it is reasonable to ask. And in this case, I assume that what Iā€™m asking for should be solvable with some kind of nginx routing setup.

Of course, if the interference is too high, I would not do it. But so far I donā€™t see a major counter argument yet. Iā€™m open however.

1 Like

Thank you for your input. I thought soo to, but I have not much knowledge on routing ngnix and apache together. I guess I better ask somewhere on a more nginx-specific forum rather than this nextcloud one?

However Iā€™m afraid that nextcloud would still need to know about the routing since for example wouldnā€™t it need to know its own root url for link generation?

Ok i re-read your first post. Here is perhaps a better idea.
I think cloud.museumsstrasse.at is a good name for nextcloud. So we do not change it.

But you can create a new subdomain (with Lets Encrypt) e.g.

https://public.museumsstrasse.at
and/or use subdir from ā€œwwwā€:
https://www.museumsstrasse.at/public

and there you program a 403-redirect (.htaccess or easier):

public/index.php

<?php
header("HTTP/1.1 301 Moved Permanently");
header("Location: `https://cloud.museumsstrasse.at/s/Ytpcwos3o9o33kL`");
header("Connection: close");
?>

or

public/.htaccess

RewriteEngine On
RewriteRule ^(.*)$ https://cloud.museumsstrasse.at/s/Ytpcwos3o9o33kL [NC,L,QSA]

or direct in your /-path without subdir public:

RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule public$ https://cloud.museumsstrasse.at/s/Ytpcwos3o9o33kL [NC,L]

Now you can access it with:

https://public.museumsstrasse.at
and/or use subdir from ā€œwwwā€:
https://www.museumsstrasse.at/public

Ok the destination link is the old link. Sorry.
But you can use Iframes.
Perhaps it is possible with a direct rewrite.

Thank you devnull,

I tried your suggestions and the second one with modyfing public/.htaccess worked, but not as I wanted it to. Because it did correctly redirect but the target url towards which the redirections points is then visibile to the visitor. I want to hide this away so that when visiting the public folder the only url the visitor sees is and always will be only cloud.musuemsstrasse.at and nothing else.

I could get this working in my following comment. Thank you for your input!

Alright, I got it working - at the expense of quite some pain.

Firstly, what I tried and what did not work (or what I could not get working):

  • Moving the nextcloud code into sub-folder (the nc image would always overwrite)
  • Creating virtual host by the apache server within the nc image. I could get correct redirections, but the target urls were always visible, which I wanted to avoid.
  • Creating redirections on the nginx reverse proxy layer.

Now, what I did instead was:

  • 1.) ā€œmovedā€ nextcloud to subpath by symbolic linking
  • 2.) recreate the reverse proxy stack with traefik

Details:

1.) ā€œmovedā€ nextcloud to subpath

In order to reach nextcloud under the url cloud.museumsstrasse.at/intern (before I had it called ā€˜internalā€™) I did the following steps:

1.1.

Created symbolic linking within the container:

ln -s /var/www/html/ /var/www/html/intern
chown www-data:root -h /var/www/html/intern

1.2.

Then opened nextcloud in a browser, registered an admin user, provided db credentials.

This step is necessary since afterwards some config files are auto generated which will be modified in the next steps.

1.3.

In /var/www/html/.htaccess is the auto-generated code-block where RewriteBase must be modified:

Without this step I got an ERR_TOO_MANY_REDIRECTS error.

<IfModule mod_rewrite.c>
  ....
  RewriteBase /intern # <-- changed from auto generated `RewriteBase /`
  ....
</IfModule>

1.4.

Now it would work mostly already, except for the fact that the web login and client authentication form freeze (somehow related to the reverse proxying). This issue is discussed here: https://github.com/nextcloud/server/issues/19091 and to fix it, the following must be added to /var/www/html/config/config.php:

...
'overwriteprotocol' => 'https' 
...

After these steps, the nextcloud instance can be reached with the desired subpath cloud.museumsstrasse.at/intern

A helpful discussion can be found here: https://github.com/nextcloud/docker/issues/401

2.) traefik reverse proxy

Now in order to show a selected public folder under the root url of cloud.museumsstrasse.at traefik can be used to replace paths on the fly while hiding the targeted urls from the visitor so that no matter what subpath is returned under cloud.museumsstrasse.at the url will always remain.

For this in traefik v2 a middleware must be defined. Since Iā€™m using docker-compose only, I did this via these labels on the proxied service:

# defines the middleware:
- "traefik.http.routers.service_nextcloud.middlewares=custom_repath" 

# the regex which the rule should match (double dollar signs for escaping):
- "traefik.http.middlewares.custom_repath.replacepathregex.regex=^/$$"

# the replacement value (the subpath of a selected public folder):
-"traefik.http.middlewares.custom_repath.replacepathregex.replacement=/intern/s/63KFWGkziffJR8j"

3.) The full docker-compose.yml

services:

  service_traefik:
    image: "traefik:v2.2"
    container_name: container_traefik
    networks:
      - network_traefik
    command:
      # an explicit reference to the docker network must be passed when multiple networks exist:
      - "--providers.docker.network=docker_network_traefik"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.websecure.address=:443"
      - "--certificatesresolvers.myresolver.acme.tlschallenge=true"
      - "--certificatesresolvers.myresolver.acme.email=XXX"
      - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
    ports:
      - "443:443"
      - "8080:8080"
    volumes:
      - "./volumes/traefik/letsencrypt:/letsencrypt"
      - "/var/run/docker.sock:/var/run/docker.sock:ro"

  service_nc_db:
    image: mariadb
    container_name: container_nc_db
    volumes:
      - ./volumes/nc_db:/var/lib/mysql
      - /etc/localtime:/etc/localtime:ro
    networks:
      - network_nc
    environment:
      - MYSQL_ROOT_PASSWORD=XXX
      - MYSQL_PASSWORD=XXX
      - MYSQL_DATABASE=XXX
      - MYSQL_USER=XXX

  service_nc:
    image: nextcloud:latest
    container_name: container_nc
    depends_on:
      - service_traefik
      - service_nc_db
    networks:
      - network_traefik
      - network_nc
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.service_nextcloud.rule=Host(`cloud.museumsstrasse.at`)"
      - "traefik.http.routers.service_nextcloud.entrypoints=websecure"
      - "traefik.http.routers.service_nextcloud.tls.certresolver=myresolver"
      - "traefik.http.routers.service_nextcloud.middlewares=custom_repath"
      - "traefik.http.middlewares.custom_repath.replacepathregex.regex=^/$$"
      - "traefik.http.middlewares.custom_repath.replacepathregex.replacement=/intern/s/63KFWGkziffJR8j"
    volumes:
      - ./volumes/nc/html:/var/www/html

networks:
  network_nc:
  network_traefik:

4.) versions

Currently I got this running with nextcloud 19.0.1 and as seen in the docker-compose.yml with traefik 2.2

Nice. But the old config was ok and not bad.

Does sharing works e.g.

Museum@cloud.museumsstrasse.at

because of subfolder /intern

Yes, everything works. :slight_smile:

1 Like

docker-compose:
NEXTCLOUD_TRUSTED_DOMAINS: nc-server
OVERWRITEHOST: nchost.dev:9998
OVERWRITEPROTOCOL: http
OVERWRITEWEBROOT: /nc

nginx:
server {
listen 80;
proxy_set_header Host $host;

location /nc {
    proxy_set_header X-Forwarded-Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    rewrite /nc/(.*) /$1 break;
    proxy_pass http://nc-server;
}

}