This guide walks through deploying TissuePlex on a public cloud server so that collaborators can access it from any browser without running Docker themselves.
Estimated time: 1–2 hours (most of that is waiting for data to upload).
Estimated cost: $106–116/month on DigitalOcean ($1,280–1,400/year).
https://tissuplex.yourdomain.com
↓
Cloud server (DigitalOcean)
Caddy (HTTPS, automatic TLS certificate)
↓
Docker Compose
frontend (nginx)
backend (FastAPI + DuckDB)
/mnt/tissuplex-data ← your Xenium dataset(s)
You need:
- A credit card for DigitalOcean billing
- A domain name (e.g.
yourdomain.com) — or a subdomain of one you already own - Your Mac Terminal (or any SSH client)
If you do not have a domain, buy one from Namecheap.
Search for yourlabname.com or similar; .com costs ~$12/year.
You only need the domain. Namecheap's DNS management (free) is all you need — no paid add-ons required.
- Go to digitalocean.com and sign up.
- Add a payment method. DigitalOcean bills monthly; you can cancel anytime.
An SSH key lets you log into the server without a password. You likely already have one on your Mac.
Check for an existing key:
cat ~/.ssh/id_ed25519.pubIf that file exists, copy its entire output (starts with ssh-ed25519 ...).
If it doesn't exist, generate one:
ssh-keygen -t ed25519 -C "tissuplex-server"
# Press Enter three times to accept defaults and skip a passphrase
cat ~/.ssh/id_ed25519.pubAdd it to DigitalOcean:
- In the DigitalOcean dashboard, click your avatar (top right) → Settings → Security
- Click Add SSH Key
- Paste the key text, name it "My Mac", save
-
In DigitalOcean, click Create → Droplets
-
Choose these settings:
Setting Value Region New York or San Francisco (or closest to your collaborators) OS Ubuntu 24.04 (LTS) Droplet type General Purpose Plan 16 GB RAM / 4 vCPU / 50 GB SSD — $96/month Authentication SSH Key → select "My Mac" Hostname tissuplex(anything you like) -
Click Create Droplet. It takes about 60 seconds to be ready.
-
Copy the IP address shown in your dashboard (e.g.
143.198.50.12).
The Droplet's built-in SSD is only 50 GB — not enough for your data plus the tile cache. Add a separate storage volume:
-
In the DigitalOcean dashboard, click Create → Volumes
-
Settings:
Setting Value Size 200 GB (gives plenty of headroom for growth) Region Same region as your Droplet Filesystem ext4 Attach to Droplet select your tissuplexdropletMount point /mnt/tissuplex-data -
Click Create Volume. (~$20/month)
DigitalOcean automatically mounts the volume at /mnt/tissuplex-data on the
server. No manual formatting needed.
You need to create a DNS record that maps your chosen subdomain to the server IP.
In Namecheap (or wherever your domain is registered):
-
Go to Domain List → Manage next to your domain
-
Click Advanced DNS
-
Add a new record:
Type Host Value TTL A Record tissuplexYOUR_SERVER_IPAutomatic This makes
tissuplex.yourdomain.compoint to your server.
DNS changes take 5–60 minutes to propagate worldwide. You can continue with the remaining steps while waiting.
Open Terminal on your Mac and connect:
ssh root@YOUR_SERVER_IPType yes if asked about the host fingerprint. You should see an Ubuntu welcome
message. You are now inside your cloud server.
Still inside the SSH session, run:
curl -fsSL https://raw.githubusercontent.com/RaredonLab/TissuePlex/main/deploy.sh | bashThe script will:
- Update the server's packages
- Install Docker
- Clone TissuePlex
- Ask you two questions: your domain name and the data path (
/mnt/tissuplex-data) - Build and start the Docker containers
When asked:
Domain name: tissuplex.yourdomain.com ← your actual subdomain
Data directory: /mnt/tissuplex-data ← press Enter, this is the default
The build takes 3–5 minutes. When it finishes you will see a "Setup complete" message.
Type exit to close the SSH session and return to your Mac.
Back on your Mac, open the file upload-data.sh in the TissuePlex repo.
Edit the first configuration line to put in your server's IP address:
SERVER_IP="143.198.50.12" # ← replace with your actual IPThen run it:
cd /path/to/TissuePlex
bash upload-data.shThis uses rsync, which is resumable. If your internet connection drops, just
run it again and it picks up where it left off. For a 30 GB dataset on a typical
home connection, expect 30–90 minutes.
Once DNS has propagated and the upload is complete, open a browser and go to:
https://tissuplex.yourdomain.com
On first load, TissuePlex will build the DZI tile pyramid from your OME-TIFF (same as locally — takes 30–120 seconds the first time). Subsequent loads are instant.
Currently anyone who knows the URL can view your data. To require a username and password:
1. Generate a hashed password (SSH into the server first):
ssh root@YOUR_SERVER_IP
docker run --rm caddy:2-alpine caddy hash-password --plaintext 'choose-a-password'Copy the long hash that is printed (starts with $2a$...).
2. Edit the Caddyfile on the server:
nano /opt/tissuplex/CaddyfileUncomment the basicauth block and fill in your chosen username and the hash:
tissuplex.yourdomain.com {
reverse_proxy frontend:80
basicauth {
labuser $2a$14$PASTE_THE_HASH_HERE
}
}
3. Restart Caddy:
cd /opt/tissuplex
docker compose -f docker-compose.prod.yml restart caddyAnyone visiting the URL will now be prompted for a username and password.
You can add multiple users by adding more lines inside the basicauth block.
Upload the new dataset folder to the server:
rsync -avz --progress \
"/path/to/new_dataset/" \
"root@YOUR_SERVER_IP:/mnt/tissuplex-data/new_dataset/"No restart needed — TissuePlex auto-discovers all datasets in the data directory. The new dataset will appear in the dataset picker immediately.
SSH into the server and run:
cd /opt/tissuplex
git pull
docker compose -f docker-compose.prod.yml --env-file .env.prod up -d --buildYour data and tile cache are preserved across updates.
# View live logs (Ctrl-C to stop)
docker compose -f /opt/tissuplex/docker-compose.prod.yml logs -f
# Check container status
docker compose -f /opt/tissuplex/docker-compose.prod.yml ps
# Stop everything
docker compose -f /opt/tissuplex/docker-compose.prod.yml down
# Restart a specific service
docker compose -f /opt/tissuplex/docker-compose.prod.yml restart backendBrowser shows "site can't be reached"
DNS may not have propagated yet. Check with: nslookup tissuplex.yourdomain.com
It should return your server's IP. Wait a bit longer if it doesn't.
Browser shows a security warning (not HTTPS)
Caddy needs to reach Let's Encrypt to get a certificate. This requires:
- DNS is pointing at the server (see above)
- Ports 80 and 443 are open — check DigitalOcean's firewall settings
Blank viewer / no image
The tile pyramid may still be building. Wait 60 seconds and refresh.
Dataset not listed
Check that your data uploaded completely and is in the right location:
ssh root@YOUR_SERVER_IP
ls /mnt/tissuplex-data/
ls /mnt/tissuplex-data/RQ32878-002_Slide_2_PPLR_726-2/You should see experiment.xenium in the dataset folder.
Out of memory errors / backend crashes
Reduce DUCKDB_MEMORY_LIMIT in /opt/tissuplex/.env.prod and restart:
docker compose -f /opt/tissuplex/docker-compose.prod.yml --env-file /opt/tissuplex/.env.prod up -d