but why

I’ve been using Nextcloud (formerly ownCloud) for years. I truly loved it and saw its evolution since very early stage.
However, the scope of this project dramatically changed over time.
What used to be a simple self-hosted dropbox PHP page has become a big collaborative platform regrouping many applications: files, groupware, visioconference, and so on.
This is still an amazing project, but I have to admit it quite drifted out of my requirements. All I need is a self-hosted files sync software with a web UI to get the files. Nothing more. Also, if PHP was the way to go 10 years ago, there are some cooler tech nowadays.

That’s the plan

I’m now using Syncthing & Filestash.
Because life is better with containers, everything is dockerized.
Syncthing is in charge of syncing files across all our devices.
Filestash is the frontend to access, manage and share files.
There is also an nginx reverse proxy, getting Let’s Encrypt certs and forwarding to filestash.

Ports exposed to public:

  • 22000, syncthing sync protocol.
  • 443, nginx reverse proxy entrypoint.
  • 22, SSH/SFTP.

Syncthing binds mount the host folder containing the files to sync.
Filestash loads its frontend onto the browser. The frontend has an SFTP client that can be used to get the files.


The file structure I’m using, for reference:

├── filestash
│   ├── docker-compose.yml
│   └── state
├── letsencrypt
│   ├── config
│   └── docker-compose.yml
└── syncthing
    ├── config
    └── docker-compose.yml


  • docker
  • docker-compose


Syncthing’s docke-compose:

version: "2"
    image: syncthing/syncthing
    container_name: syncthing
    restart: unless-stopped
      - PUID=1000
      - GUID=1000
      - 22000:22000
      - /mnt/files:/var/syncthing
      - ./config:/var/syncthing/config
docker-compose up -d

Note the port mapping: we expose the 8384 port to the host localhost only. So we’ll need to port forward with SSH. That’s because an admin UI is exposed to this port without any authentication by default. You probably don’t want to expose that publicly. Alternatively, you can change to 8384:8384, which will expose to, and setup authentication on the UI right away. I prefer to stick with the local UI and SSH tunnel. The SSH tunnel command for reference:

ssh -L 8384:localhost:8384 john.doe@example.com

In order to synchronize files, we need at least a second syncthing instance. It could be an Android phone (there is a syncthing app on the Play Store), Mac or PC. Then, we can go to https://localhost:8384 on each machine and follow the guide.

And that’s pretty much it.


I’ve got synchronized files across devices, great!
Now it’s time to setup a way to browse my data remotely.
One of the easiest and maybe underrated way could be to use SFTP. There are plenty of clients that allow to remotely access to your files, either from a computer or a phone. This is cool but I love how convenient the Nextcloud web UI was.
In that case, Filestash is a good candidate.

Same here, I use docker & docker-compose (that’s actually the official way of installing filestash).
First we need to create a docker network. It will be useful later.

docker network create docker_network

Then, the filestash docker-compose config:

version: "2"
    external: true
    image: machines/filestash
    container_name: filestash
    restart: unless-stopped
      - APPLICATION_URL=filestash.example.com
      - ONLYOFFICE_URL=http://onlyoffice
#    volumes:
#       - ./state:/app/data/state
       - docker_network
    container_name: filestash_oods
    image: onlyoffice/documentserver
    restart: always
       - docker_network
docker-compose up -d

At this point, filestash is up and running in the docker network, but not reachable from public.

Nginx + Let’s Encrypt

Time to setup the reverse proxy with letsencrypt. The docker-compose file:

version: "2"
    external: true
    image: linuxserver/letsencrypt
    container_name: letsencrypt
      - NET_ADMIN
      - PUID=1000
      - PGID=1000
      - TZ=Europe/London
      - URL=example.com
      - SUBDOMAINS=filestash
      - VALIDATION=http
      - ./letsencrypt/config/filestash.subdomain.conf:/config/nginx/proxy-confs/filestash.subdomain.conf
      - 443:443
      - 80:80
    restart: unless-stopped
       - docker_network

The filestash.subdomain.conf file:

server {
    listen 443 ssl;
    listen [::]:443 ssl;

    server_name filestash.*;

    include /config/nginx/ssl.conf;

    client_max_body_size 0;

    location / {

        include /config/nginx/proxy.conf;
        resolver valid=30s ipv6=off;
        set $upstream_app filestash;
        set $upstream_port 8334;
        set $upstream_proto http;

        add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

        proxy_pass $upstream_proto://$upstream_app:$upstream_port;

We’re good to go!

docker-compose up -d

After a few minutes, filestash.example.com will get its certificate (if you own the domain and configured the zone properly). Filestash can now be accessed via https with a browser. There is a couple of config steps on the UI, quite straighforward. To access the local files, the trick is to use SFTP. I strongly recommend to use pubkey authentication rather than a password. Just input the pubkey into the SFTP password field (yes that’s quite unusal).


And here we are, a pretty good self-hosted file hosting service. I didn’t get through all the setup details, but I tried to sum up the major steps.
After several weeks of usage I am very happy with this setup. The performances are much better than Nextcloud. The gain can be felt both in term of sync speed or file browsing experience.
Syncthing is blazing fast and I allow fine tune sycnrhonisations between many devices.
Filestash is fast, intuitive and mobile friendly. It’s actually a PWA, so you can install it on your device and it will mimic a mobile app.

As a result I have even been able to downgrade my remote instance size, dividing the number of cores and memory (and bill!) by two, with no performance hit.