polluxd is the "reference" server for libpxd, a gemini server library

implemented in C. despite its "reference" title, polluxd should be quite

performant and is designed to be usable as a secure standalone server.

This is an example polluxd configuration for polluxd versions 9e96106

(2025-05-23) or later. Want to know if that fits your version or not? see if

polluxd has a '-V' option. if yes, then this is an example compatible with

your version unless there's a more recent version that supercedes it.

The following is a summary of the configuration options.

drop_user and drop_group specify the user/group to run the program as. this

requires running the server as root (or a user that can chroot)

drop_user=pxd

drop_group=pxd

this is the hostname that the gemini server will expect on requests. if

unset it will accept any host. note, this is just the url request, any TLS

SNI information is not inspected

host=ingrix.info

the IP address to listen on. 'any' can also be used

this one listens only on localhost

listen_addr=127.0.0.1

the following two are equivalent

#listen_addr=any

#listen_addr=0.0.0.0

port to listen on

port=1965

the certificate and key files that should be used for TLS connections

these are required, and can be generated with the openssl command line

utilites. see util/generate_cert_and_key.sh for a script that will generate

a private key and self-signed certificate

cert_file=cert.pem

key_file=key.pem

the optional chroot directory to serve from. the program will chroot() after reading

the cert/key and setting up the listening socket

chroot_dir=/var/gemini

docroot is where the / path of the request is considered to start. dots ..

are stripped from the URL. so traversal outside of docroot shouldn't be

possible

note: since our example chroot_dir is /var/gemini this will localize the

requests to /var/gemini/pub as seen by the server's operating system. if

chroot_dir is not set then this will serve {files,cgi,etc} from the global

/pub/ diectory

docroot=/pub/

if specified, this is where stdout/stderr get redirected once polluxd is

running. this is opened as the executing (e.g. root) user before the chroot

is performed, so this file may lie outside of the chroot directory. this

last point may change in future releases as it's not clear to the author that

this is not a source of security vulnerabilities

logfile = /var/log/polluxd.log

LOCATION BLOCKS ###

locations blocks define acceptable gemini request paths, the action

the server should take on those paths, and a limited transformation of those

paths that the server should perform to translate the gemini request

into a filesystem path. the server may use the following actions:

deny - deny access to a single file or an entire directory tree via a Not

Authorized status code

file - serve a file from the filesystem. files served via this means may be

a pipe, but cannot be a socket. the filesystem file may be a symlink but

it may no point outside of the docroot

cgi - execute a cgi script. the directory containing the cgi script and the

cgi scrip itself must be owned by root:root or the server user/group and may

not be world-writable by other. cgi scripts must be files, and cannot be

symlinks

pipe - similar to file, but may read data only from a pipe or unix socket.

this contains fewer checks than 'file' by necessity so it is moved into its

own category

the request path specification can contain filename wildcards (see

fnmatch(3)). asterisks will not match multiple subdirectories (e.g.

/pub/*/foo will match a request of /pub/bar/foo but will not match a request

of /pub/bar/baz/foo)

#

a file-serving block for the root request path. one of these should ALWAYS

be specified. while its inclusion in the configuration file is mandatory,

the action you choose is immaterial

location / {

action = file

prefix = /docs # will serve a file from /docs/

}

deny blocks will deny access to any request paths that start with /secret

with a '61 Not authorized' error

note: you may specify strip and prefix here, as are done below, and the

request path translation will still be done, but it will make no material

difference since the action is based on the request path alone

location /secret {

action = deny

strip = 1 # meaningless here

prefix = /doesntmatter # also meaningless here

}

the following will translate any request under the /cgi/ directory to the

/cgi/ directory on the filesystem.

the combo of 'strip = 1' and 'prefix = /cgi2' effectively removes the leading

/cgi and replaces it with /cgi2

location /cgi {

action = cgi

strip = 1

prefix = /cgi2

}

gemini request paths at /pipe/* will be served from

location /pipe {

action = pipe

strip = 1 # will translate /pipe (request) -> / (server side)

prefix = /foo # will translate / -> /foo

ultimately a request for gemini://myhost.com/pipe/somepipe will be

cause the server OS to serve data from the file /var/gemini/foo/somepipe

if the server is chrooted to /var/gemini, or /foot/somepipe if the server

is not chrooted

}


Source