Installation
To get the full pxvoid experience, it should be running on a server connected to the internet. Performance isn't the deciding factor here; rather, depending on your plans, you should ensure you have enough free storage space, as images require room. In most cases, a simple VPS with 8GB of RAM and 2 cores is sufficient. Since pxvoid is written in Python and doesn't use any esoteric libraries, it should run on any standard Linux server system. You also need a domain or subdomain for your gallery like pxvoid.your-domain.tld or px.your-domain.tld and the DNS entries that point on your server.
Here are two ways to install and configure pxvoid on NixOS and other Linux distributions (like Debian or Ubuntu)
NixOS
Within NixOS it is easy to build a working pxvoid gallery. We just create a .nix file with all the configuration stuff, create some folders, set the permissions and start pxvoid as a service within your configuration.nix. After the rebuild everything will work like a charm.
1. CREATE PROJECT DIRECTORY
It's best to create a separate directory for pxvoid so you have full control over where your files are located. This makes debugging much easier later on.
mkdir -p /srv/pxvoid/{app,media/photos,keys,db}
2. CLONE PXVOID
In the next step, we retrieve the pxvoid code from Codeberg and clone it into our newly created directory.
cd /srv/pxvoid/app
git clone https://codeberg.org/0x17/pxvoid.git .
3. GENERATE ACTIVITYPUB KEY
To ensure your pxvoid instance is properly federated, it needs two keys: a private.pem and a public.pem. We create these with the following commands:
cd /srv/pxvoid/keys
openssl genrsa -out private.pem 2048
openssl rsa -in private.pem -pubout -out public.pem
chmod 600 private.pem
chmod 644 public.pem
4. CREATE ENVIRONMENT FILE
We create the .env file with the following content
vim /srv/pxvoid/app/.env
PXVOID_BASE_URL=https://your-domain.tld
PXVOID_TOKEN=SUPERSAFETOKEN
ZEROFRAME_DB=/srv/pxvoid/db/gallery.db
Then we assign the appropriate access rights to the .env file.
chmod 600 /srv/pxvoid/app/.env
5. THE NIX FILE
In the next step, we create the basic nix file which configures the starting of the service, virtual host, Python packages, and everything else:
{ config, lib, pkgs, ... }:
let
cfg = config.services.pxvoid;
pxvoidPython = pkgs.python312.withPackages (ps: with ps; [
fastapi
httpx
uvicorn
jinja2
python-multipart
cryptography
requests
pillow
]);
in
{
options.services.pxvoid = {
enable = lib.mkEnableOption "pxvoid service";
domain = lib.mkOption {
type = lib.types.str;
example = "pxvoid.example.org";
description = "Public domain for pxvoid nginx virtual host.";
};
appDir = lib.mkOption {
type = lib.types.str;
default = "/srv/pxvoid/app";
description = "Path to pxvoid git checkout.";
};
envFile = lib.mkOption {
type = lib.types.str;
default = "/srv/pxvoid/app/.env";
description = "Environment file loaded by systemd (PXVOID_BASE_URL, PXVOID_TOKEN, ZEROFRAME_DB).";
};
user = lib.mkOption {
type = lib.types.str;
default = "pxvoid";
description = "System user running pxvoid.";
};
group = lib.mkOption {
type = lib.types.str;
default = "pxvoid";
description = "System group running pxvoid.";
};
mediaDir = lib.mkOption {
type = lib.types.str;
default = "/srv/pxvoid/media";
description = "Base media directory (photos in media/photos).";
};
dbDir = lib.mkOption {
type = lib.types.str;
default = "/srv/pxvoid/db";
description = "Directory that stores gallery.db.";
};
keyDir = lib.mkOption {
type = lib.types.str;
default = "/srv/pxvoid/keys";
description = "Directory for ActivityPub private.pem/public.pem.";
};
acmeEmail = lib.mkOption {
type = lib.types.str;
default = "admin@example.org";
description = "Email for ACME certificate registration.";
};
};
config = lib.mkIf cfg.enable {
users.users.${cfg.user} = {
isSystemUser = true;
group = cfg.group;
home = "/srv/pxvoid";
createHome = false;
};
users.groups.${cfg.group} = {};
systemd.tmpfiles.rules = [
"d ${cfg.mediaDir} 0750 ${cfg.user} ${cfg.group} -"
"d ${cfg.mediaDir}/photos 0750 ${cfg.user} ${cfg.group} -"
"d ${cfg.dbDir} 0750 ${cfg.user} ${cfg.group} -"
"d ${cfg.keyDir} 0750 ${cfg.user} ${cfg.group} -"
];
systemd.services.pxvoid = {
description = "pxvoid FastAPI service";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "simple";
User = cfg.user;
Group = cfg.group;
WorkingDirectory = cfg.appDir;
EnvironmentFile = cfg.envFile;
Environment = [ "PYTHONPATH=${cfg.appDir}" ];
ExecStart = "${pxvoidPython}/bin/uvicorn --app-dir ${cfg.appDir} app.main:app --host 127.0.0.1 --port 8000";
Restart = "always";
RestartSec = "3";
};
};
services.nginx.enable = true;
services.nginx.recommendedProxySettings = true;
services.nginx.virtualHosts.${cfg.domain} = {
forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://127.0.0.1:8000";
extraConfig = ''
client_max_body_size 25M;
'';
};
};
security.acme = {
acceptTerms = true;
defaults.email = cfg.acmeEmail;
};
};
}
In the first part of the file, we declare the option that we will later import into our configuration.nix, and in the second section, we set the parameters for the actual pxvoid configuration. The file now needs to be imported and made available. Therefore, two more entries are added to the central configuration.nix:
in {
imports = [
./path/to/pxvoid.nix
];
And we start the service itself as follows:
services.pxvoid = {
enable = true;
domain = "pxvoid.your-domain.tld";
appDir = "/srv/pxvoid/";
envFile = "/srv/pxvoid/app/.env";
acmeEmail = "mail@your-domain.tld"
6. PERMISSIONS AND REBUILD
If we now perform a rebuild, it might complete without problems, but pxvoid won't start. The problem here is that the user isn't yet assigned to our app directory. Therefore, we'll now assign the correct permissions and then rebuild the system:
chown -R pxvoid:pxvoid /srv/pxvoid/media /srv/pxvoid/db /srv/pxvoid/keys
chmod 750 /srv/pxvoid/media /srv/pxvoid/media/photos /srv/pxvoid/db /srv/pxvoid/keys
and the rebuild:
nixos-rebuild switch
other Linux Distributions (Debian-like)
These installation instruction is Debian and debian-based Systems. If you use another Distro you have to replace commands like apt with your systemequivalent like rpm, pacman and so on. This installation guide is not tested yet. If you have trouble or got errors while installing pxvoid you can contribute a working guide.
1. INSTALL DEPENDENCIES:
First, we install the necessary base so that pxvoid can run:
sudo apt update
sudo apt install -y python3 python3-venv python3-pip nginx openssl
2. DIRECTORY STRUCTURE
pxvoid needs clear paths for media, keys and database
sudo mkdir -p /srv/pxvoid/{app,media/photos,keys,db}
sudo chown -R $USER:$USER /srv/pxvoid
3. DEPLOYMENT
Now you can clone pxvoid to the directory and setup venv and the python requirements:
cd /srv/pxvoid/app
git clone https://codeberg.org/0x17/pxvoid.git .
python3 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install -r requirements.txt
4. GENERATE ACTIVITYPUB KEY
You have to generate RSA Keys. Without the keys your federation fails:
cd /srv/pxvoid/keys
openssl genrsa -out private.pem 2048
opensl rsa -in private.pem -pubout -out public.pem
chmod 600 private.pem
chmod 644 public.pem
5. CREATE ENVIRONMENT FILE
Create a .env file (i.e.: in /srv/pxvoid/app/) with following content:
PXVOID_BASE_URL=https://deine-domain.tld
PXVOID_TOKEN=SET-STRONG-TOKEN-HERE
ZEROFRAME_DB=/srv/pxvoid/db/gallery.db
Please note the following:
- PXVOID_BASE_URL must point exactly to your public URL.
- PXVOID_TOKEN protects the Upload and Delete API
- PXVOID_DB explicitly sets the DB path to the server path
6. START APP WITH UVICORN
In this step we start pxvoid via uvicorn:
cd /srv/pxvoid/app
source .venv/bin/activate
export $(grep -v '^#' .env | xargs)
uvicorn app.main:app --host 127.0.0.1 --port 8000
After that, the app should run on localhost. You can test this with the following one-liner:
curl -I http://127.0.0.1:8000/
If you get HTTP/1.1 200 OK, then pxvoid is already running.
7. SYSTEMD SERVICE
You simply create a new file using your preferred editor:
nvim /etc/systemd/system/pxvoid.service
The contents of the file look like this:
[Unit]
Description=pxvoid FastAPI service
After=network.target
[Service]
User=www-data
Group=www-data
WorkingDirectory=/srv/pxvoid/app
EnvironmentFile=/srv/pxvoid/app/.env
ExecStart=/srv/pxvoid/app/.venv/bin/uvicorn app.main:app --host 127.0.0.1 --port 8000
Restart=always
RestartSec=3
[Install]
WantedBy=multi-user.target
After that, you can set the correct permissions and start the service:
sudo chown -R www-data:www-data /srv/pxvoid/media /srv/pxvoid/db /srv/pxvoid/keys
sudo chmod 750 /srv/pxvoid/media /srv/pxvoid/media/photos /srv/pxvoid/db /srv/pxvoid/keys
sudo systemctl daemon-reload
sudo systemctl enable --now pxvoid
sudo systemctl status pxvoid
You can see if pxvoid is running correctly in the logs:
journalctl -u pxvoid -f
8. NGINX REVERSE PROXY
Create the following file:
nvim /etc/nginx/sites-available/pxvoid
server {
listen 80;
server_name deine-domain.tld;
client_max_body_size 25M;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
If the config is written correctly, you can start the proxy as follows:
sudo ln -s /etc/nginx/sites-available/pxvoid /etc/nginx/sites-enabled/pxvoid
sudo nginx -t
sudo systemctl reload nginx
Of course, you should expose your pxvoid instance to the outside via TLS:
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d deine-domain.tld
Congratulations! You should now have a working pxvoid gallery of your own!