Production Deployment
Deploy a self-hosted Emberly instance to a Linux server using Docker Compose.
This guide covers deploying Emberly to a Linux server using Docker Compose. By the end you will have a running instance accessible over HTTPS with PostgreSQL, Redis, and S3-compatible file storage configured.
Prerequisites
- A Linux server (Ubuntu 22.04 LTS recommended) with at least 2 GB RAM
- A domain name pointing to your server's IP (
Arecord) - Docker and Docker Compose installed
- An S3-compatible object storage bucket (AWS S3, Cloudflare R2, Vultr Object Storage, Tigris, or self-hosted MinIO)
Step 1: Server Preparation
Step 2: Create the Project Directory
Step 3: Docker Compose File
Create /opt/emberly/docker-compose.yml:
Self-building from source
If you prefer to build the image yourself: clone the repo, run docker build -t emberly ., and replace the image: line above with build: . or use image: emberly.
Step 4: Environment File
Create /opt/emberly/.env:
Generate a strong NEXTAUTH_SECRET:
Set strict permissions on the env file:
Step 5: Run Database Migrations
Before starting the full stack, run migrations:
Step 6: Start the Stack
Check that all containers are healthy:
The app is now running on port 3000. Open http://YOUR_SERVER_IP:3000 and complete the Setup Wizard to create your admin account.
Step 7: Reverse Proxy with HTTPS
Use Nginx or Caddy to terminate TLS and proxy to port 3000. Caddy is simpler because it provisions Let's Encrypt certificates automatically.
Caddy (Recommended)
Install Caddy, then create /etc/caddy/Caddyfile:
Nginx + Certbot
Create /etc/nginx/sites-available/emberly:
Step 8: Configure Storage
After the setup wizard completes, configure S3-compatible storage in Admin Panel → Settings → Integrations → Storage:
| Field | Description |
|---|---|
| Provider | AWS S3, Cloudflare R2, Vultr, Linode, MinIO, etc. |
| Bucket name | The bucket to store files in |
| Region | Your bucket's region |
| Endpoint | Custom endpoint (leave blank for AWS) |
| Access key ID | Storage access key |
| Secret access key | Storage secret key |
| Public files URL | The CDN/public URL prefix for served files |
The public files URL is what gets prepended to file paths in share links. For Cloudflare R2, this is your R2 public bucket URL or a custom domain. For AWS, it is the bucket's public URL or a CloudFront distribution.
Step 9: Configure Email
In Admin Panel → Settings → Integrations → Email, configure Resend or SMTP:
Resend:
- Get an API key from resend.com
- Set the from address (must be on a verified domain)
SMTP:
- Use any SMTP provider (Postmark, Mailgun, Gmail, etc.)
- Set host, port, username, and password
Email is used for verification emails, expiration reminders, and collaborator invites.
Step 10: Optional Integrations
Configure these in the Admin Panel as needed:
| Integration | Purpose |
|---|---|
| Stripe | Billing and subscription plans |
| VirusTotal | File malware scanning |
| Cloudflare | Custom domain TLS provisioning |
| Sentry | Error tracking |
Updating
Always run migrations before restarting the app on an update.
Backups
Back up the PostgreSQL database regularly:
Also back up your S3 bucket. Use your provider's snapshot or bucket replication features.
Troubleshooting
| Problem | Check |
|---|---|
| App won't start | docker compose logs app — look for missing env vars or DB connection errors |
| Database connection error | Verify DATABASE_URL and that the postgres container is healthy (docker compose ps) |
| Uploads fail | Check storage configuration in Admin Panel; verify bucket permissions and public URL |
| Email not sending | Verify Resend API key or SMTP credentials in Admin Panel |
| Custom domains not working | See Cloudflare Setup |