FAQ - infrastructure
In this FAQ we answer common questions related to the Pryv.io platform. You can contact us on Github Discussion if your question is not listed here.
Pryv.io v2 — Since v2 (2026) Pryv.io ships as a single binary (
pryvio/open-pryv.ioDocker image, ornode bin/master.js). Registration, DNS and the admin endpoints are all built into the core — there is no separateregister,core,hfs,previewordnscontainer. Where a procedure below still references the v1 multi-container layout (pryvio_*containers,run-pryv,${PRYV_CONF_ROOT}scripts) it is kept for operators still on v1; the v2 equivalent is noted inline. For v2 installs see INSTALL.
Platform prerequisites
Section titled “Platform prerequisites”In addition to the Infrastructure procurement guide (available on request), a Pryv.io platform requires its own domain name, such as pryv.me to work. Apps will access data through the https://${username}.${domain} endpoint, e.g. https://user-123.pryv.me. This can be totally hidden from the end user.
To encrypt data in transit, we require a wildcard SSL certificate for the domain *.domain, this can be either bought or generated using let’s encrypt.
Since we use our own DNS servers to resolve the domain associated with the platform, it must be possible to set name servers for the domain. This must be verified before buying the domain, as some providers do not allow it.
Do SSL certificates need to be signed by publicly trusted CA or can we use self-signed certificates?
Section titled “Do SSL certificates need to be signed by publicly trusted CA or can we use self-signed certificates?”All devices that interact with the Pryv.io platform must be able to verify the certificates and thus see/trust the CA, even if it is internal. That would involve: the mobile application, all machines that perform analytics and display of the data collected.
SSL certificates are mentioned to be wildcard ones. Are we able to define all the subdomains beforehand and rather create SSL with SANs?
Section titled “SSL certificates are mentioned to be wildcard ones. Are we able to define all the subdomains beforehand and rather create SSL with SANs?”Pryv.io uses a subdomain per user account that is created. So no, you cannot use SAN certificates unless you’re able to know the possible user base ahead of time.
On what cloud offerings can Pryv.io be installed?
Section titled “On what cloud offerings can Pryv.io be installed?”Pryv.io can be installed on any cloud offering that runs at least Docker 1.12.6. The real consideration here is compliance and the security of the data storage.
We have run Pryv.io on the following public clouds: AWS, Microsoft Azure, Gandi.net, Exoscale.ch, Joyent, Fengqi.asia.
What constraints should be considered when choosing a host?
Section titled “What constraints should be considered when choosing a host?”You must take into account the legislation covering the people whose data will be stored in the Pryv.io platform, such as US HIPAA, EU GDPR, Swiss DPA. This often includes requirements on the geographic location where the data is stored.
How do you address encryption of the data at rest?
Section titled “How do you address encryption of the data at rest?”Pryv offers several options, from turnkey to fully self-managed:
- Turnkey encrypted image variant (recommended starting point): the published
pryvio/open-pryv.io-encryptedimage layers an encrypted volume inside the container, so all of Pryv.io’s data directories (events, attachments, series, audit, platform DB) sit on ciphertext at rest. It is off by default and you opt in with one switch; the basepryvio/open-pryv.ioimage is unchanged. Pluggable backends (LUKS / gocryptfs) and key sources (environment, file, a cloud KMS such as AWS KMS, or TPM/Tang via Clevis). See thecontainer-encrypted-volumeproject and the Open Pryv.ioINSTALL.md“Encryption at rest” section. - Host / cloud disk encryption: Linux has a solid disk-encryption story (LUKS, cloud-provider disk encryption, PostgreSQL TDE for an external database). If Pryv.io’s data lives on such a volume, it is encrypted at rest. This stays fully under your infrastructure control.
- Application-level end-to-end encryption: your application(s) encrypt the data before sending it and decrypt it after reading it back, so the server never sees plaintext. Pryv.io provides a data type to carry the encryption metadata. See: http://pryv.github.io/event-types/#encrypted
The first two protect against stolen / decommissioned disks, off-host backups, and snapshots (the scope of HIPAA §164.312(a)(2)(iv) and GDPR Art.32). They do not protect a running host — for that, or for a server-blind model, use application-level end-to-end encryption. Platform secrets (TLS / ACME keys) are always encrypted at rest regardless.
Has Open Pryv.io had a security review?
Section titled “Has Open Pryv.io had a security review?”Yes. The maintainers’ code-level security review of Open Pryv.io, with its findings by severity, the release that fixed each one, the operator actions and the residual risks, is published in the repository as SECURITY-AUDIT.md. It is revised with every release that carries a security fix. To report a vulnerability, follow SECURITY.md (privately, not as a public issue).
Self-managed top-domain
Section titled “Self-managed top-domain”This only applies to multi-core deployments that run the embedded DNS (i.e. dnsLess.isActive: false). Single-node v2 installs use dnsLess mode and do not need any of this.
The embedded DNS served by each core must resolve all requests for the domain. Entries in the top-domain will look like:
ns1-${DOMAIN} TTL_SECONDS IN A ${IP_ADDRESS_CORE_1}ns2-${DOMAIN} TTL_SECONDS IN A ${IP_ADDRESS_CORE_2}
${DOMAIN} TTL_SECONDS IN NS ns1-${DOMAIN}${DOMAIN} TTL_SECONDS IN NS ns2-${DOMAIN}On single-node or PoC installations of a multi-core platform, you will have only one core; both Type A entries point to the same IP address:
ns1-${DOMAIN} TTL_SECONDS IN A ${IP_ADDRESS_CORE_1}ns2-${DOMAIN} TTL_SECONDS IN A ${IP_ADDRESS_CORE_1}
${DOMAIN} TTL_SECONDS IN NS ns1-${DOMAIN}${DOMAIN} TTL_SECONDS IN NS ns2-${DOMAIN}You can verify that the core is set to resolve DNS queries for your domain using: dig NS ${DOMAIN}. The answer section must include:
${DOMAIN} ${TTL_SECONDS} IN NS ns1-${DOMAIN}.${TOP_DOMAIN}${DOMAIN} ${TTL_SECONDS} IN NS ns2-${DOMAIN}.${TOP_DOMAIN}Customize registration, login, password-reset pages
Section titled “Customize registration, login, password-reset pages”We provide default web apps for registration, login, password-reset and auth request. The code is available on https://github.com/pryv/app-web-user-account (formerly app-web-auth3).
To customize it, fork the repository and activate GitHub Pages on the gh-pages branch (an empty commit is enough to kick the build off).
To serve the app under the /access/ path of your domain (the proxy recipes below), build the copy you publish with that base path, not with npm run build:pages (whose /app-web-user-account/ base would make the browser request the assets outside /access/, so the page stays blank):
npx tsc -b && npx vite build --base=/access/ && cp dist/index.html dist/404.html && touch dist/.nojekyllThen publish dist/ to your fork’s gh-pages branch. The app’s routes then live under /access/ (/access/register, /access/auth, …).
You then need to point the /access/ path of your Pryv.io deployment at your fork:
-
v2 — this is handled by your own reverse proxy. Add an
/access/location to the NGINX config shown in INSTALL — Running behind nginx:location /access/ {proxy_pass https://${CUSTOMER_ACCOUNT}.github.io/app-web-user-account/;add_header Content-Security-Policy "frame-ancestors 'none'" always;add_header X-Frame-Options "DENY" always;}if ($request_uri !~* "^/access/static/.*$") {rewrite ^/access/.*$ /access/index.html;}Keep the two
add_headerlines (and repeat there anyadd_headeryour server block sets, such as HSTS: nginx drops the server-level ones in alocationthat has its own): the consent pages (/auth,/oauth2-authorize,/cmc-accept,/cmc-scope-update) must not be framable by another site, or it can hide them under a decoy and trick a click on Accept (clickjacking). GitHub Pages does not send these headers, and a<meta>tag cannot setframe-ancestors, so your proxy must. For the same reason, do not list the GitHub Pages URL itself inaccess:trustedAuthUrls.Then set
access.defaultAuthUrlin your platform configuration to the deployed consent page (e.g.https://${DOMAIN}/access/auth) so that auth requests return the rightauthUrl.Alternatively, skip the external proxy: in DNS mode the core can serve the app itself on its own origin (e.g.
https://account.${DOMAIN}/) through thehostedSitessetting, either from a local folder holding a root-based build (npm run build:root, which each app-web-user-account release also ships as a tarball) or as a fixed proxy to an upstream serving such a root-based build. In dnsLess mode the site is served under/account/on the core’s public URL, which is the API’s own origin rather than a separate one. See the Hosted sites section of INSTALL. Hosted sites refuse framing for you from open-pryv.io 2.0.0-rc.29 on. This is how the Pryv Lab serves https://account.pryv.me/; setaccess.defaultAuthUrltohttps://account.${DOMAIN}/authandservice.accounttohttps://account.${DOMAIN}/accordingly. -
v1 — edit the bundled NGINX config at
pryv/nginx/site.conf. Change:proxy_pass https://pryv.github.io/app-web-user-account/;to:
proxy_pass https://${CUSTOMER_ACCOUNT}.github.io/app-web-user-account/;and add the following in the
sw.${DOMAIN}server scope:if ($request_uri !~* "^/access/static/.*$") {rewrite ^.*$ /access/index.html;}In the same location as the
proxy_pass, also addadd_header Content-Security-Policy "frame-ancestors 'none'" always;andadd_header X-Frame-Options "DENY" always;so that the consent pages cannot be framed by another site (see v2 above).
The following pages will show the changes that you apply to this repository (behind the /access/ path; on a hosted site, the same paths on its own origin, e.g. https://account.${DOMAIN}/register):
- Registration: https://${DOMAIN}/access/register
- Reset password: https://${DOMAIN}/access/reset-password
- Consent authorization: https://${DOMAIN}/access/auth
Host apps, resources on the same domain and reuse the SSL certificate
Section titled “Host apps, resources on the same domain and reuse the SSL certificate”You can reuse the platform’s SSL certificate by hosting additional apps behind the same reverse proxy as Pryv.io.
-
v2 — add
locationblocks to your own NGINX (or other reverse proxy). See INSTALL — Running behind nginx for the base configuration, then insert:location /MY_APP/ {proxy_pass MY_APP_URL_WITH_PROTOCOL;proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;proxy_buffering off;} -
v1 — the built-in NGINX proxy can be configured to serve apps under
https://sw.${DOMAIN}/MY_APP/by adding the samelocationclause inpryv/nginx/conf/site.conf.
System administrators
Section titled “System administrators”Port 53 is already in use (v1 only)
Section titled “Port 53 is already in use (v1 only)”On v1 installs, the DNS container could fail to bind port 53 when Docker’s embedded DNS was using the same interface. The fix was to pin the DNS service to an explicit external-interface IP in docker-compose:
ports: - "EXTERNAL_INTERFACE_IP_ADDRESS:53:5353/udp"In v2, the embedded DNS server runs in-process inside the core (when dnsLess.isActive: false / multi-core). The DNS port is taken by the core process itself, on both UDP and TCP; conflicts with Docker’s embedded DNS no longer apply. If another process holds port 53 (UDP or TCP), the core stops at boot with a DNS server failed to bind error: free the port or bind the core’s DNS to a specific interface (dns.ip).
docker login X11 error
Section titled “docker login X11 error”On a Pryv install using Ubuntu 18.X when running docker login: Ubuntu refuses to let you enter the password because it wants you to use a secure means of password entry. The error reads something like ‘Cannot autolaunch D-Bus without X11 $DISPLAY’ (docker-compose issue #6023).
Our workaround is:
sudo apt-get remove golang-docker-credential-helperssudo apt install docker-composeThe second line is needed because the first removes docker-compose as well.
Are my containers running?
Section titled “Are my containers running?”Show running containers: “docker ps”, if the container exited, you can use “docker ps -a”. This will allow to find the name of the container.
Why is container XYZ not running?
Section titled “Why is container XYZ not running?”By default, our containers write logs to the console (stdout and stderr), so the reason for a failure can be printed using docker logs ${CONTAINER_NAME}. See Where do the core’s logs go?
Where do the core’s logs go?
Section titled “Where do the core’s logs go?”The core (open-pryv.io v2) logs to the targets set under logs: in its configuration: logs.console (the console, read by the journal or docker logs), logs.file (daily files <path>.YYYY-MM-DD, kept 14 days by default), logs.fileSync (a synchronous file for low-volume levels) and logs.custom (a module of your own). The built-in defaults turn every target off; the production defaults, applied with NODE_ENV=production as in the Docker image, turn on the console at info without colours and an errors-only file, var-pryv/logs/api-server.errors.log. The install wizard (init) writes the same kind of logs: section from 2.0.0-rc.54, with the error file under the data folder (<data folder>/logs/api-server.errors.log, 14 days kept); a configuration written by an earlier wizard can add it.
- No target configured: from 2.0.0-rc.54, the master and the workers still write their warnings and errors to
stderr, sodocker logsor the journal keeps them. Earlier releases dropped them. - Colours:
logs.console.format.colordecides whether console lines carry ANSI colour codes (defaulttrue, for interactive development;falsein the production defaults). There is nologs.console.colorizekey: a configuration that sets it should useformat.color: falseinstead. - JSON:
LOG_FORMAT=json(orlogs.console.format.json) prints one JSON object per line; an error logged with its Error object carries its name, message and stack incontext, in the JSON console from 2.0.0-rc.54 and inlogs.filefrom 2.0.0-rc.55. - Master lines: from 2.0.0-rc.54 they read
[info]: [master:master] <msg>(and[warn]:/[error]:), no longer[master] WARNING: <msg>or[master] ERROR: <msg>: adjust a monitoring grep on those prefixes. - Masking: credentials (tokens, passwords,
auth=parameters, URL user info and similar keys) are masked on every target before a line is written.
logs.file.rotation.maxSize caps the size of each daily file (see the change log, 2.0.0-rc.52).
Permission denied error
Section titled “Permission denied error”During deployment and update, it is possible that some folders have incorrect permissions, preventing the Pryv.io process from reading configuration and data files. The error looks like:
Error: EACCES: permission denied- v2: inspect the container with
docker logs -f --tail 50 <container-name>(the default ispryvio_open_pryv_io). Ensure that the host paths mounted into the container (typicallydata/users,data/previews,data/rqlite-data— see INSTALL) are owned by the UID the container runs as. - v1: logs live under
pryvio_${CONTAINER_NAME}; run the providedensure-permissionsscript on the host and reboot services.
Can I run the core with no persistent disk at all (“diskless”)?
Section titled “Can I run the core with no persistent disk at all (“diskless”)?”Yes — single-core dnsLess deployments in full PostgreSQL mode can keep every durable byte off the app host: set storages.platform.engine: postgresql (platform data joins the user data in PostgreSQL; no rqlite process runs) and storages.file.engine: s3 (event attachments on any S3-compatible object store — AWS S3, MinIO, Ceph RGW). The remaining configured paths are caches that can live on tmpfs, so the app container runs with a read-only root filesystem. The install wizard offers both options when you pick dnsLess + postgresql, and bin/config-to-env.js can convert the generated config into an env file for fully config-file-free docker run --env-file deployments. Multi-core platforms keep rqlite (Raft replication); bin/migrate-platform.js moves platform data between the two engines when a deployment changes shape. See INSTALL — Diskless.
How do I upgrade the platform DB’s rqlite to 10.x?
Section titled “How do I upgrade the platform DB’s rqlite to 10.x?”From open-pryv.io 2.0.0-rc.34, the Docker image and the native setup bundle rqlite 10 (10.5.1, previously 9.4.5) for the platform DB; from 2.0.0-rc.55 they bundle 10.6.0. rqlite 10 is crash-safe where 9.x could, after a node was killed during a snapshot, restore stale data into its copy of the platform DB.
The first start on 10.x converts the rqlite data directory one way: an older release cannot open it afterwards, so a rollback needs the data directory restored from a backup. Before upgrading:
- run 2.0.0-rc.33 or later first (older releases could interrupt rqlite’s snapshot when stopped);
- back up every node (
/db/backup, plus an archive of the data directory taken while the core is stopped); - check the snapshot store after a clean stop;
- in a multi-core platform, upgrade the follower cores first and every core back to back.
The step-by-step procedure, including the checks and the rollback, is in INSTALL — Upgrading the bundled rqlite (9.x to 10.x). Single-core deployments that keep their platform data in PostgreSQL (storages.platform.engine: postgresql) run no rqlite and are not affected.
Within 10.x (10.5.1 to 10.6.0, from 2.0.0-rc.55) no data-directory step is needed: back up as above, then upgrade one core at a time, the followers first and the leader last; 10.5.1 and 10.6.0 nodes work together while the cluster is upgraded. The Docker image carries the new binary. A native install does not: the setup script never replaces an existing bin-ext/rqlited, so replace it yourself while the core is stopped (same INSTALL section).
How do I reset data on my Pryv.io platform?
Section titled “How do I reset data on my Pryv.io platform?”This step will erase all data from your platform. Perform this at your own risk and make sure that you know what you are doing.
v2 — stop the core, remove the data directories that back your configured storage engines, then restart. With the defaults from INSTALL:
# stop the core (Docker or systemd, depending on your deployment)rm -rf data/users/* # SQLite DBs + per-user files (audit, index, account)rm -rf data/previews/* # image previewsrm -rf data/rqlite-data/* # platform DB (rqlite Raft + snapshot)# if using external MongoDB / PostgreSQL: drop and recreate the database there# then restart the corev1:
cd ${PRYV_CONF_ROOT}./stop-pryvrm -rf pryv/core/data/*rm -rf pryv/mongodb/data/*rm -rf pryv/influxdb/data/*./run-pryvHow can I use the demo dashboard app (app-web) on my Pryv.io platform?
Section titled “How can I use the demo dashboard app (app-web) on my Pryv.io platform?”App-web is hosted on GitHub pages and can be used for your Pryv.io platform as described in its documentation.
Can I use my own SSL termination with Pryv.io?
Section titled “Can I use my own SSL termination with Pryv.io?”Yes.
v2 — the core has three modes, all covered in INSTALL:
- Built-in HTTPS (the quick / out-of-the-box path): let the core terminate TLS by setting
http.ssl.keyFile/http.ssl.certFilein the override config, or use theletsEncrypt.*block for managed ACME. High-frequency series traffic is also routed in-process from the public port to the HFS worker on:4000— setcluster.hfsWorkers: 1(or more) to enable HFS; no extra ingress required. - Behind your own reverse proxy (recommended for high-throughput / production): leave
http.sslunset, run the core on plain HTTP (http.port: 3000), and terminate TLS in your nginx / Caddy / ALB. nginx is more efficient than the in-process dispatcher and unlocks edge features (rate-limiting, header munging, static assets). A ready-to-use nginx vhost — including the HFS path rules and Socket.IO WebSocket upgrade — is atdocs/nginx-ingress-sample.conf. If the core also serves hosted sites (such as the account app) behind that proxy, it sees plain HTTP and therefore sends noStrict-Transport-Securityon their answers: from open-pryv.io 2.0.0-rc.34, sethostedSites.<name>.hsts: alwaysfor an HTTPS-only site (or add the header in your proxy). - backloop.dev for dev/test.
For HFS specifically, SDKs read features.noHF on /service/info to decide whether the deployment serves HF — this is auto-derived from cluster.hfsWorkers (set to true when hfsWorkers === 0), so SDKs short-circuit cleanly on no-HF deployments instead of failing opaquely. Operators can hand-override with service.features.noHF: true|false in the override config.
v1 — edit the NGINX config shipped with the stack:
In the nginx/conf/nginx.conf file, comment out the following lines by adding a # at their beginning:
#ssl_certificate /app/conf/secret/DOMAIN-bundle.crt;#ssl_certificate_key /app/conf/secret/DOMAIN-key.pem;In the nginx/conf/site-443.conf file, change the listen directive from 443 ssl to 80 for each server block:
server { listen 80; # ...}My security policy requires that all outgoing traffic goes through a proxy, will Pryv.io work?
Section titled “My security policy requires that all outgoing traffic goes through a proxy, will Pryv.io work?”Yes.
v2 — set http_proxy / https_proxy in the environment of the core process (or pass them through your Docker orchestrator / systemd unit). There are no per-role helper scripts to edit; the single bin/master.js process and its workers inherit the environment.
v1 — add http_proxy / https_proxy to the reverse-proxy service in docker-compose.yml:
reverse_proxy: image: "eu.gcr.io/pryvio/nginx:1.3.40" container_name: pryvio_nginx # ... environment: - http_proxy - https_proxyand export them at the top of the run-config-leader, run-config-follower, run-pryv and restart-* scripts:
export http_proxy=${YOUR-PROXY-HOSTNAME-WITH-PORT}export https_proxy=${YOUR-PROXY-HOSTNAME-WITH-PORT}
