Skip to content

Potato Server Requirements for Your IT Team

Forward this page to your IT or research computing team. Potato runs as one process with a writable disk behind HTTPS and keeps each annotation on that server.

Potato is a self-hosted web application for annotation studies. One Potato process serves one study, keeps every annotation in files and SQLite databases inside the study's folder, and needs a reverse proxy in front of the process for HTTPS. A default install sends nothing to outside services, and data leaves the server only through features the study's configuration turns on.

This page is written for the people who run servers at a university, hospital, or company. A researcher who wants a study hosted can send the page unchanged, together with the project folder and the answers listed in the next section. Put Your Potato Study Online covers the routes a researcher can take without an IT team.

Information from the research team

The research team sends the project folder, which the Study Designer exports as Download project (.zip). The folder holds config.yaml, the study's configuration, and the data files the configuration names. Alongside the folder, the research team answers these questions:

  • How many annotators take part, and do annotators sign in with institutional accounts or with usernames the researcher lists?
  • When does the study start and end?
  • Do the items or the answers contain personal or sensitive information, and under which ethics approval does the study run?
  • Does the configuration use AI features that send items to a model provider? Blocks such as ai_support, chat_support, and solo_mode in config.yaml mark such features.

Software

Potato is a Python web application built on Flask. The simplest install is the published Docker image, ghcr.io/davidjurgens/potato, built for linux/amd64 and linux/arm64. The image is about 900 MB, runs Python 3.11 under gunicorn as the unprivileged user potato (uid 1000), and listens on port 7860. The project folder mounts at /app:

bash
docker run -d --name potato-study --restart unless-stopped \
  -p 127.0.0.1:8000:7860 \
  -v /srv/potato/my-study:/app \
  -e POTATO_SECRET_KEY="$(openssl rand -hex 32)" \
  ghcr.io/davidjurgens/potato:2.14.0

The command publishes the study only on port 8000 of the host's loopback interface. The reverse proxy is then the single way in. The latest tag follows Potato's development branch, and a study should pin a version tag. Generate POTATO_SECRET_KEY once and keep the value, because a new key on every start logs every annotator out at each restart. The container refuses to start when /app is not writable by uid 1000. Run the container with --user "$(id -u):$(id -g)" or give the folder to uid 1000. Run Potato in Docker covers the image's tags and environment variables.

Without Docker, Potato installs from PyPI on Python 3.9 or newer with pip install potato-annotation. Add the hosting extra for backups and single sign-on, and the saml extra for SAML sign-in, which builds against the system's libxmlsec1. potato start config.yaml runs a development server that closes the connection after every response. For annotators working over the internet, run Potato under gunicorn as Production Setup shows, or put a reverse proxy in front of potato start.

Process and worker count

Potato keeps the item pool, the assignment queue, and every annotator's state in the memory of one process. A second gunicorn worker or a second replica holds a separate copy of all three, hands out items the first process already assigned, and overwrites the first process's saves. The image therefore refuses to start with GUNICORN_WORKERS above 1. For more concurrency, raise GUNICORN_THREADS from the default of 8. Potato's own Docker documentation puts eight threads at the dozens of simultaneous annotators a typical study has. Run several studies as several containers, each container with a separate folder and port.

Server size

Memory grows with the number of items. Potato's measurements put each item at 0.9 to 1.4 KB in memory, and the server on Potato's reference dataset used about 365 MB. potato deploy, Potato's own cloud command, picks a machine with 1 or 2 vCPUs and 2 GB of memory on most hosts, which is a reasonable starting size.

The disk has to be persistent and writable, and the disk needs room for the data files, any media files the items include, and the annotations. A container whose disk is wiped on restart loses every annotation unless the built-in backup described below is on.

Network access and HTTPS

Expose Potato through a reverse proxy such as nginx, Apache, or Caddy. The proxy holds the TLS certificate and forwards requests to the container's port. Only ports 443, and 80 for redirects and certificate renewal, need to be open to the internet. Reverse Proxy shows how to serve Potato under a URL path such as https://host.example.edu/my-study/, through POTATO_PROXY_FIX=1 with an X-Forwarded-Prefix header or through POTATO_URL_PREFIX. The live agent viewers stream with server-sent events. Turn off proxy buffering on the /api/ location if the study uses those viewers.

GET /health needs no authentication and returns {"status": "ok"} once the server is ready, and 503 while the server is still loading data. Point a load balancer or monitoring check at that route.

Potato needs no outbound network access by default. The scripts, stylesheets, and fonts that annotators' browsers load come from the server itself, and Air-Gapped and Offline Deployment lists how Potato keeps the default install offline. Outbound access is needed only for features the configuration turns on:

  • AI features call the model endpoint the configuration names, unless the endpoint is a local model server such as Ollama.
  • Backups write to the Hugging Face dataset or S3 bucket the configuration names.
  • Single sign-on contacts the identity provider.
  • Remote data sources read from the bucket or database the configuration names.
  • Items that link to media by URL make the annotator's browser fetch the media from that URL.

Files Potato writes

Potato writes everything into the study's folder:

Path in the project folderContents
<output_annotation_dir>/<username>/user_state.jsoneach annotator's answers, rewritten in full on every save
user_config.json, next to the output folderthe account list, with salted PBKDF2-SHA256 password hashes
project.sqlite, datasets.sqliteproject data such as memos, codebooks, and review state, in SQLite's WAL mode
admin_api_key.txtthe generated admin key, readable only by the server's user, when no key is set
potato.logthe server log

One directory therefore holds the whole study. output_annotation_dir is a setting in config.yaml, and the Study Designer sets the value to output.

Backups

A nightly backup of the project folder covers the annotation files and the account list. Copy the two SQLite files with SQLite's own backup command rather than as files, because a file copy of a database in WAL mode can be corrupt or miss recent writes:

bash
sqlite3 /srv/potato/my-study/project.sqlite ".backup /backups/my-study/project.sqlite"

Potato's built-in backup copies the annotation output and snapshots of the databases to an S3-compatible bucket or a private Hugging Face dataset every five minutes. On start, the backup restores from the bucket when the disk is empty, and the backup never overwrites annotations already on disk. The block below sends the copy to a university object store. Add the block to the study's config.yaml, and set POTATO_S3_ACCESS_KEY_ID and POTATO_S3_SECRET_ACCESS_KEY in the container's environment:

yaml
backup:
  schedule_minutes: 5
  restore_on_boot: true
  sinks:
    - type: s3
      bucket: research-backups
      prefix: potato/my-study
      endpoint_url: https://s3.example.edu

The published image includes the backup clients. A pip install needs the hosting extra.

Sign-in options

Potato supports several ways for annotators to sign in, set in config.yaml:

MethodSuitsWhat the server needs
Named accounts with passwords (the default)a known list of annotatorsuser_config.users lists the usernames, with allow_all_users: false. Potato refuses registration for anyone not listed
Username only (require_password: false)low-stakes tasks and classroom exercisesnothing more. The username list still applies
OAuth and OpenID ConnectGoogle, GitHub, or Hugging Face accounts, or any OpenID Connect provider such as Microsoft Entra ID or Oktaa client ID and secret, and the callback URL https://<host>/auth/callback/<provider> registered with the provider
SAML 2.0Shibboleth, ADFS, and SAML apps in Okta and Microsoft Entrathe saml extra, or the image built with --build-arg POTATO_EXTRAS=saml. Potato serves the service-provider metadata at /saml/metadata and receives assertions at /saml/acs
Links from a crowdsourcing platformProlific, SONA, CloudResearch Connect, and similar platformsthe platform's study URL pointed at the server

Passwords are hashed with PBKDF2-SHA256 at 100,000 iterations and a per-user salt. A forgotten password is reset through a single-use link that Potato shows on screen, and Potato sends no email. Behind a proxy that ends TLS, set sp_base_url for SAML to the public address, or the identity provider's response fails Potato's destination check. SSO and OAuth, Password Management, and Crowdsourcing Integration give the configuration for each method.

Admin access

The admin dashboard at /admin shows progress, agreement, and every annotator's answers, and downloads the results. The dashboard opens with an admin key. Set the key through POTATO_ADMIN_API_KEY, or let Potato generate admin_api_key.txt in the project folder on first use, and pass the key to the researcher through a channel your institution trusts. Keep debug: false in config.yaml on any server, because debug mode opens the admin pages without a key to requests that look local, and a proxy on the same machine can make every request look local.

Data protection

Potato is self-hosted, so the annotations, the account list, and the items stay on the server you run. Potato contains no analytics or usage reporting, and a default install makes no outbound connections. Data leaves the server only through the features listed under Network access and HTTPS, each of which the study's configuration has to turn on and name a destination for.

Check three points against the study's ethics approval:

  • The annotation files name each annotator by username, so usernames that are email addresses put email addresses in the data.
  • Survey pages in a study can collect personal information, and the answers sit in the same per-annotator files.
  • When the configuration sends items to a hosted model provider, the items leave the server under that provider's terms.

When the study ends, the research team downloads the results from the admin dashboard. Agree with the team how long the server copy is kept, and delete the project folder and any backup bucket after that date.

Further reading