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
}
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