Cannot Upload/Download/Edit Files on VPS

Help, I’m pulling out what little hair I have left. I’m a novice when it comes to Docker, Docker Compose, and Linux (although many moons ago I did program in assembler and my Windows skills are decent). Here are the details.

Environment:
- Ionos VPS running Ubuntu 24.04
- Docker Community, version 28.1.1
- Docker Compose, version 2.35.1
- Portainer Community Edition, version 2.27.5
- Seafile Server, version 12.0.11 64 bit
- Docker-compose.yml and .env files below (installed via Portainer)
- I didn’t install caddy, but my understanding is that won’t affect things since I’m only using http at this point
- I do have a registered domain with a public IP address

Problem:
- Portainer stack and containers installed and spooled up without problems
- Unable to upload a file to VPS via web interface. Receive generic “network error”
- Unable to download an admittedly empty file. Receive generic “This site can’t be reached” from browser.
- I can create a file on the server, but I cannot edit and save it via web interface.
- I’ve searched the net and the seafile forum, and tried appropriate remedies to no avail.

I’m hopeful that this problem can be corrected as Seafile looks like the perfect app for my needs. I appreciate any help you can offer.

Docker Compose File:
services:
db:
image: ${SEAFILE_DB_IMAGE:-mariadb:10.11}
container_name: seafile-mysql
environment:
- MYSQL_ROOT_PASSWORD=${INIT_SEAFILE_MYSQL_ROOT_PASSWORD:-}
- MYSQL_LOG_CONSOLE=true
- MARIADB_AUTO_UPGRADE=1
volumes:
- “${SEAFILE_MYSQL_VOLUME:-/opt/seafile-mysql/db}:/var/lib/mysql”
networks:
- seafile-net
healthcheck:
test:
[
“CMD”,
“/usr/local/bin/healthcheck.sh”,
“–connect”,
“–mariadbupgrade”,
“–innodb_initialized”,
]
interval: 20s
start_period: 30s
timeout: 5s
retries: 10

memcached:
image: ${SEAFILE_MEMCACHED_IMAGE:-memcached:1.6.29}
container_name: seafile-memcached
entrypoint: memcached -m 256
networks:
- seafile-net

seafile:
image: ${SEAFILE_IMAGE:-seafileltd/seafile-mc:12.0-latest}
container_name: seafile
ports:
- “7080:80”
- “7443:443”
volumes:
- ${SEAFILE_VOLUME:-/opt/seafile-data}:/shared
environment:
- DB_HOST=${SEAFILE_MYSQL_DB_HOST:-db}
- DB_PORT=${SEAFILE_MYSQL_DB_PORT:-3306}
- DB_USER=${SEAFILE_MYSQL_DB_USER:-seafile}
- DB_ROOT_PASSWD=${INIT_SEAFILE_MYSQL_ROOT_PASSWORD:-}
- DB_PASSWORD=${SEAFILE_MYSQL_DB_PASSWORD:?Variable is not set or empty}
- SEAFILE_MYSQL_DB_CCNET_DB_NAME=${SEAFILE_MYSQL_DB_CCNET_DB_NAME:-ccnet_db}
- SEAFILE_MYSQL_DB_SEAFILE_DB_NAME=${SEAFILE_MYSQL_DB_SEAFILE_DB_NAME:-seafile_db}
- SEAFILE_MYSQL_DB_SEAHUB_DB_NAME=${SEAFILE_MYSQL_DB_SEAHUB_DB_NAME:-seahub_db}
- TIME_ZONE=${TIME_ZONE:-Etc/UTC}
- INIT_SEAFILE_ADMIN_EMAIL=${INIT_SEAFILE_ADMIN_EMAIL:-me@example.com}
- INIT_SEAFILE_ADMIN_PASSWORD=${INIT_SEAFILE_ADMIN_PASSWORD:-asecret}
- SEAFILE_SERVER_HOSTNAME=${SEAFILE_SERVER_HOSTNAME:?Variable is not set or empty}
- SEAFILE_SERVER_PROTOCOL=${SEAFILE_SERVER_PROTOCOL:-http}
- SITE_ROOT=${SITE_ROOT:-/}
- NON_ROOT=${NON_ROOT:-false}
- JWT_PRIVATE_KEY=${JWT_PRIVATE_KEY:?Variable is not set or empty}
- SEAFILE_LOG_TO_STDOUT=${SEAFILE_LOG_TO_STDOUT:-false}
- ENABLE_SEADOC=${ENABLE_SEADOC:-true}
- SEADOC_SERVER_URL=${SEAFILE_SERVER_PROTOCOL:-http}://${SEAFILE_SERVER_HOSTNAME:?Variable is not set or empty}/sdoc-server
depends_on:
db:
condition: service_healthy
memcached:
condition: service_started
networks:
- seafile-net

networks:
seafile-net:
name: seafile-net

Environment File:

SEAFILE_IMAGE=seafileltd/seafile-mc:12.0-latest
SEAFILE_DB_IMAGE=mariadb:10.11
SEAFILE_MEMCACHED_IMAGE=memcached:1.6.29

SEAFILE_VOLUME=/opt/seafile-data
SEAFILE_MYSQL_VOLUME=/opt/seafile-mysql/db

SEAFILE_MYSQL_DB_HOST=db
INIT_SEAFILE_MYSQL_ROOT_PASSWORD=[my_db_password]
SEAFILE_MYSQL_DB_USER=sfdbuser
SEAFILE_MYSQL_DB_PASSWORD=[my_user_password]

TIME_ZONE=America/New_York

JWT_PRIVATE_KEY=[my_40char_password]

SEAFILE_SERVER_HOSTNAME=[mydomain.com]
SEAFILE_SERVER_PROTOCOL=http

INIT_SEAFILE_ADMIN_EMAIL=[my_email_address]
INIT_SEAFILE_ADMIN_PASSWORD=[my_admin_password]

SEADOC_IMAGE=seafileltd/sdoc-server:1.0-latest
SEADOC_VOLUME=/opt/seadoc-data

ENABLE_SEADOC=true

NOTIFICATION_SERVER_IMAGE=seafileltd/notification-server:12.0-latest
NOTIFICATION_SERVER_VOLUME=/opt/notification-data

tl;dr - In the .env, add the port to SEAFILE_SERVER_HOSTNAME, so it is “SEAFILE_SERVER_HOSTNAME=mydomaincom:7080”.

Also, you can remove the “7443:443” since (by default at least) there isn’t anything inside the seafile container listening to port 443, but it shouldn’t be breaking anything to have that there.

Why I think that (if you care):

To docker that “ports” part means “listen on the server’s real IP to port 7080, and forward whatever comes in there to port 80 of this container” (and 7443 to 443). There’s an nginx inside the seafile container that listens on 80 and forwards things to a couple of different programs based on the path in the URL.

When seafile gives your browser a link to follow, it uses “SEAFILE_SERVER_PROTOCOL” and “SEAFILE_SERVER_HOSTNAME” to create that link. So you need SEAFILE_SERVER_HOSTNAME to have the port in it. That way it should tell your browser to download from http://mydomain.com:7080/some/path/to/an/api, instead of http://mydomain.com/some/path/to/an/api, which would default to port 80, which isn’t where the server is listening.

You are my hero! Made the change you suggested and it worked perfectly. I’m somewhat familiar with how ports work, but I thought that docker would use the ports spec in the compose yaml file and append it to the server name. Clearly I was wrong. It’s nice to learn a bit more. Thanks for taking the time to read my post and respond. Much appreciated!

You are welcome, happy to help.