Early Bird - the Full Version is $19 right now, before it goes to $39. See pricing →

Deployment

Running the self-hosted server: HTTPS on your own network or behind a reverse proxy, systemd services, and security hardening.

Running your own server needs the Full Version + Server license.

Desktop app users: This page is for self-hosted server deployments only. If you are using the desktop app, you do not need any of this.

Before You Begin

The self-hosted server is part of Full Version + Server. To run it you need a server license key, which is issued automatically by email when you buy Full Version + Server.

The container will not start without two environment variables: ACCEPT_EULA=true and LICENSE_KEY set to your server key. Both are included in the Docker Compose example below.

Ways to Serve It

Needlework Studio serves HTTPS out of the box. Where the certificate comes from depends on who uses the server.

  • Only your own computers. The container makes its own certificate. No proxy, no domain name, nothing to renew. The pairing code is what confirms it. This is the default: leave HTTPS unset.
  • Other people, in a browser. They need a certificate their browser already trusts, or they get a security warning. A reverse proxy gets one and keeps it renewed. Set HTTPS=true when you use one.
  • Plain HTTP, nothing encrypting. Set SELF_TLS=false. Only sensible on a network you already trust end to end, and it is how the server behaved before version 0.18.0.

If you are not sure, the first one is simpler and is what most home servers want.

Updating a server from before 0.18.0? It used to serve plain HTTP, and now it makes its own certificate and serves HTTPS. The address you have bookmarked changes from http:// to https://, and the old one stops loading. Open the same address with https instead. To keep it exactly as it was, set SELF_TLS=false. Servers already behind a reverse proxy with HTTPS=true are unaffected.

On Your Own Network

Leave HTTPS unset and the container generates its own certificate the first time it starts, then serves HTTPS on port 6850. The certificate is kept in the data volume, so it survives restarts and updates.

A docker-compose.yml for this is short:

services:
  needlework-studio:
    image: ghcr.io/greenglasst/needleworkstudio:latest
    ports:
      - "6850:6850"
    volumes:
      - needlework-data:/data
    environment:
      - ACCEPT_EULA=true
      - LICENSE_KEY=your-server-license-key
    restart: unless-stopped

volumes:
  needlework-data:

Your desktop computers then connect to https://your-server-ip:6850.

If you start from the docker-compose.yml that comes with the app instead, check its port line. It is set to 127.0.0.1:6850:6850, which only lets the server's own machine reach it. That suits a reverse proxy on the same machine, but other computers cannot pair with it. Switch to the "6850:6850" line shown above to pair over your network.

Browsers will warn that the certificate is not valid for your server's address unless you list that address in TRUSTED_HOSTS before the first start, for example TRUSTED_HOSTS=192.168.1.50. See Administration for what else that setting does.

The Pairing Code

The pairing code is how a desktop confirms it reached your server and not something else answering on the same address. You enter it once, when you connect. If it does not match, nothing is sent.

Same code in three places:

  • The container's startup output, boxed. docker compose logs needlework-studio
  • The license agreement page, the first time you sign in to the server in a browser.
  • The server's Settings page, once signed in.

The browser is easiest: sign in and read it off the page. On a network you share with people you do not know, use the logs instead. See Desktop Sync for the desktop side.

If you reinstall the server and lose the data volume, it makes a fresh certificate and the pairing code changes. Every computer that was paired will refuse to sync and say so. Disconnect and pair each one again with the new code.
If the code changed and you did not reinstall the server, do not pair again. A code you cannot explain is exactly what the pairing check exists to catch, and re-pairing would accept whatever is answering. Stop and find out why first.

The first time you open the server in a browser you will see a certificate warning, because the certificate the container made was not issued by a certificate authority. That is expected for a server on your own network, and you can continue past it. The pairing code, not the browser, is what confirms your computer is talking to your own server.

Behind a Reverse Proxy

Use a reverse proxy when people other than you will open the server in a browser. It gives you a certificate browsers trust, renews it for you, and lets you serve on standard ports (80/443) alongside other services.

Set the HTTPS=true environment variable when you do. It tells the container a proxy is handling encryption, so it serves plain HTTP internally, skips making its own certificate, and marks session cookies as Secure. There is no pairing code in this setup, because the proxy's certificate already proves which server you reached.

Caddy (Recommended)

Caddy is a modern web server that automatically provisions and renews Let's Encrypt certificates. It requires minimal configuration and is the simplest way to add HTTPS to your deployment.

Create a docker-compose.yml that runs both Caddy and Needlework Studio:

services:
  caddy:
    image: caddy:latest
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - caddy-data:/data
      - caddy-config:/config
    restart: unless-stopped

  needlework-studio:
    image: ghcr.io/greenglasst/needleworkstudio:latest
    volumes:
      - needlework-data:/data
    environment:
      - HTTPS=true
      - ACCEPT_EULA=true
      - LICENSE_KEY=your-server-license-key
    restart: unless-stopped

volumes:
  needlework-data:
  caddy-data:
  caddy-config:

Replace your-server-license-key with the server license key from your purchase email.

Create a Caddyfile in the same directory:

needlework.example.com {
    reverse_proxy needlework-studio:6850
}

Replace needlework.example.com with your actual domain name. Caddy will automatically obtain an SSL certificate from Let's Encrypt and handle renewals. No additional port mapping is needed for the Needlework Studio container because Caddy communicates with it over the internal Docker network.

Admin setup: After starting your containers for the first time and creating a user, you need to promote that user to admin. Either run docker compose exec needlework-studio python manage_users.py admin, or add - ADMIN_USERNAME=your_username to the environment section of the needlework-studio service in your docker-compose.yml.

Other Reverse Proxies

Needlework Studio works with any reverse proxy (Nginx, Apache, Traefik, etc.). The key requirements are:

  • Proxy all traffic to the application on port 6850.
  • Forward the Host, X-Forwarded-For, and X-Forwarded-Proto headers.
  • Set HTTPS=true on the Needlework Studio container when terminating SSL at the proxy.

Systemd Service

For manual (non-Docker) installations on Linux, you can run Needlework Studio as a systemd service so it starts automatically on boot and restarts on failure.

Create a unit file at /etc/systemd/system/needlework-studio.service:

[Unit]
Description=Needlework Studio
After=network.target

[Service]
Type=simple
User=needlework
Group=needlework
WorkingDirectory=/opt/needlework-studio
ExecStart=/opt/needlework-studio/venv/bin/gunicorn -w 1 --threads 4 -b 0.0.0.0:6850 app:app
Restart=on-failure
RestartSec=5
Environment=HTTPS=true
Environment=LICENSE_KEY=your-server-license-key
Environment=NEEDLEWORK_DATA_DIR=/opt/needlework-studio/data

[Install]
WantedBy=multi-user.target

Replace your-server-license-key with your server key. The server exits at startup without a valid one. This unit expects a reverse proxy in front of it to handle HTTPS, which is what HTTPS=true means. The self-made certificate and pairing code described above come from the Docker container, not from a manual install.

Enable and start the service:

sudo systemctl daemon-reload
sudo systemctl enable needlework-studio
sudo systemctl start needlework-studio

Check that the service is running:

sudo systemctl status needlework-studio

View application logs:

sudo journalctl -u needlework-studio -f

Docker with Auto-Restart

The Docker Compose examples in this documentation include restart: unless-stopped, which ensures the container automatically restarts after a crash or a host reboot. This is the simplest way to achieve high availability with Docker.

The container will restart in all of the following scenarios:

  • The application process crashes or exits with a non-zero code.
  • The Docker daemon restarts.
  • The host machine reboots.

The only time the container will not restart is if you explicitly stop it with docker compose down or docker stop. If you want the container to restart even after an explicit stop, use restart: always instead.

Security Notes

Needlework Studio includes several built-in security measures. Understanding these will help you configure your deployment securely.

CSRF Protection

All state-changing requests are protected by CSRF tokens. These tokens are automatically included in forms rendered by the application and validated on the server for every POST, PUT, and DELETE request.

Password Hashing

User passwords are hashed using Argon2, the winner of the Password Hashing Competition. Argon2 is resistant to GPU-based and ASIC-based brute-force attacks and is considered the current best practice for password storage.

Rate Limiting

The application enforces rate limits to prevent abuse:

  • Login attempts: 5 requests per minute per IP address. This mitigates brute-force password attacks.
  • Thread inventory updates: 60 requests per minute per user. This prevents accidental or automated flooding of the API.

Rate limiting is implemented in-memory, which is why a single Gunicorn worker (-w 1) is required. See Installation > Production with Gunicorn for details.

HTTP Security Headers

Needlework Studio sets the following security headers:

  • Content-Security-Policy (CSP): Restricts the sources from which scripts, styles, images, and other resources can be loaded, mitigating cross-site scripting (XSS) attacks.
  • Strict-Transport-Security (HSTS): Instructs browsers to only access the application over HTTPS, preventing protocol downgrade attacks. This is sent only when HTTPS=true. It is never sent with the container's own certificate, because it would lock browsers out with no way past the certificate warning.
  • X-Frame-Options: Prevents the application from being embedded in iframes on other sites, protecting against clickjacking attacks.

Secure Cookies

When HTTPS=true is set, all session cookies are marked with the Secure flag, ensuring they are never transmitted over unencrypted HTTP connections. Cookies also use the HttpOnly and SameSite attributes to prevent JavaScript access and cross-site request attachment.

Upload Limits

To protect against denial-of-service through large file uploads:

  • File size limits: PDF imports up to 25 MB, images up to 10 MB, and trace and reference images up to 5 MB each. A backup restored from the admin panel can be up to 500 MB. Files over the limit are rejected.
  • Decompression bomb protection: Image processing is capped at 25 megapixels. This prevents malicious images with extreme dimensions from consuming excessive memory during processing. Note that some modern cameras and phones exceed this in a single shot, so a very large photo may need downsizing before it will convert.

Crash Reports

A server does not send crash reports unless you set SENTRY_DSN to your own Sentry address. At startup the server does contact the license server once to check LICENSE_KEY. See Administration for both settings.

Updating

Your data is preserved across updates regardless of the installation method.

Docker

Pull the latest image and recreate the container:

docker compose pull && docker compose up -d

Manual Install

Pull the latest code and install any new dependencies:

git pull && pip install -r requirements.txt

Alternatively, download a fresh copy from the Downloads page and replace the application files, keeping your data directory intact.

Desktop App

The desktop app checks for updates on its own. When one is available it downloads in the background and tells you when it is ready to install. You can also check any time with Check for Updates…, in the Needlework Studio menu on a Mac or the Help menu on Windows and Linux. If you prefer, download the latest installer from the Downloads page and install it over the existing version. Your data is stored separately from the application and will not be affected.