Sortix NGINX with HTTPS and PHP via Let's Encrypt
Jonas 'Sortie' Termansen on 2025-06-27In this tutorial, we will install a Sortix server running a website using NGINX with PHP and HTTPS TLS certificates provided by Let's Encrypt. In fact, that's exactly how this blog is hosted on a Sortix server.
It's assumed you already have an IP available for your server, which has a
DNS record registered such as example.com
.
Installation
First download and install the latest Sortix build.
It's recommended to seed your downloaded .iso with secure randomness from your source machine, which prewarms the entropy for the live environment and installation. Doing so provides extra security that the cryptographic keys are secure:
wget https://pub.sortix.org/sortix/release/nightly/builds/sortix-nightly-x86_64.iso -O sortix.iso &&
wget https://pub.sortix.org/sortix/release/nightly/scripts/tix-iso-add -O tix-iso-add &&
wget https://pub.sortix.org/sortix/release/nightly/scripts/tix-iso-bootconfig -O tix-iso-bootconfig &&
./tix-iso-bootconfig --random-seed bootconfig &&
./tix-iso-add sortix.iso bootconfig
# install sortix here
rm -rf bootconfig sortix.iso # When no longer useful to avoid leaks.
Secure first time ssh
Alternatively, if you are going to ssh into your new installation, this is an
excellent time to also seed your .iso with your ssh keys.
The tix-iso-liveconfig(8)
script makes it easy to add your keys.
You can add your public key to the root user's authorized_keys
, so the server
automatically trusts your client. You can also pregenerate the sshd keys, which
lets you generate your known_hosts
entries ahead of time, so your client will
trust your server's fingerprint on the first connection. If ssh ever asks you
whether to trust a fingerprint, you should have used this approach instead.
wget https://pub.sortix.org/sortix/release/nightly/builds/sortix-nightly-x86_64.iso -O sortix.iso &&
wget https://pub.sortix.org/sortix/release/nightly/scripts/tix-iso-add -O tix-iso-add &&
wget https://pub.sortix.org/sortix/release/nightly/scripts/tix-iso-bootconfig -O tix-iso-bootconfig &&
wget https://pub.sortix.org/sortix/release/nightly/scripts/tix-iso-liveconfig -O tix-iso-liveconfig &&
chmod +x tix-iso-add tix-iso-bootconfig tix-iso-liveconfig &&
./tix-iso-liveconfig \
liveconfig \
--hostname=example.com \
--sshd-keygen \
--sshd-key-known-hosts-file=new_known_hosts \
--sshd-key-known-hosts-hosts="example.com,192.0.2.1 example.com 192.0.2.1" \
--root-ssh-authorized-keys="$HOME/.ssh/id_ecdsa.pub" &&
./tix-iso-bootconfig bootconfig --liveconfig=liveconfig --random-seed &&
./tix-iso-add sortix.iso bootconfig &&
cat new_known_hosts >> ~/.ssh/known_hosts
# install sortix here
rm -rf liveconfig bootconfig sortix.iso # When no longer useful to avoid leaks.
You can add the IP address of your new server here if you already know it ahead
of time. You can pass --enable-sshd
to
tix-iso-bootconfig(8)
if you wish to automatically start sshd
during the live environment. The
installer will offer you to copy the ssh keys from the live environment into the
new installation.
NGINX with HTTP
The first step is to set up nginx with HTTP only, and prepare the configuration so you can obtain a TLS certicate via the ACME protocol
Login as root to begin the nginx setup.
Create the nginx configuration to begin customizing your installation:
mkdir -p /etc/nginx
mkdir -p /etc/nginx/sites-available
mkdir -p /etc/nginx/sites-enabled
nginx(8)
will search for configuration files/directories in the /etc/nginx
and
/etc/default/nginx
directories. The files in /etc
belong to the system
administrator, but the default fallback files in /etc/default
belong to the
operating system and must not be edited (and will be reset on upgrades). The
operating system ships the default nginx configuration in /etc/default/nginx
.
You can override the default files in there by creating their counterparts in
/etc/nginx
.
The -enabled directores are searched for configuration files. It's useful to put the actual files in the -available directories, and symlink them into the enabled directories, so one can easily enable/disable configuration.
Copy and enable the acme site which listens on port 80, answers ACME challenges, and redirects other all traffic to HTTPS:
cp /etc/default/nginx/sites-available/acme /etc/nginx/sites-available/acme
ln -s ../sites-available/acme /etc/nginx/sites-enabled/acme
The acme configuration file looks like this:
server {
listen 80;
location /.well-known/acme-challenge/ {
root /var/www/acme;
rewrite ^/\.well-known/acme-challenge/(.*)$ /$1 break;
}
location / {
return 301 https://$host$request_uri;
}
}
Test the nginx configuration is valid:
nginx -t
The nginx server can now be enabled with service(8) and tested:
service nginx enable
service nginx status
curl --head http://example.com
You should see a Location: header that redirects to https://example.com
.
Inspect the /var/log/nginx.log
and /var/log/init.log
files if nginx isn't
running correctly.
CAA record
It's recommended to create a CAA DNS record for your domain that states which certificate authorities are allowed to issue TLS certificates for your domain. This is an important security hardening, which ensures certicates are not issued by certificate authorities that you are not using.
See the Let's Encrypt CAA Guide.
Let's Encrypt via acme-client
The second step is to obtain a TLS certificate. You can do this using any Certificate Authority on the market.
This example uses the Let's Encrypt certificate authority, as it is free to use, easy and automated, and has a good reputation. The acme-client(8) port obtains TLS certificates via the standard ACME protocol supported by certificate authorities such as Let's Encrypt.
Copy the example acme-client.conf(5) configuration:
cp /etc/examples/acme-client.conf /etc/acme-client.conf
Edit the /etc/acme-client.conf
configuration appropriately for your domain:
authority letsencrypt {
api url "https://acme-v02.api.letsencrypt.org/directory"
account key "/etc/acme/letsencrypt-privkey.pem"
#contact "mailto:me@example.com"
}
authority letsencrypt-staging {
api url "https://acme-staging-v02.api.letsencrypt.org/directory"
account key "/etc/acme/letsencrypt-staging-privkey.pem"
#contact "mailto:me@example.com"
}
domain example.com {
alternative names { www.example.com }
domain key "/etc/letsencrypt/live/example.com/privkey.pem" ecdsa
domain chain certificate "/etc/letsencrypt/live/example.com/chain.pem"
domain full chain certificate "/etc/letsencrypt/live/example.com/fullchain.pem"
# Test with the staging server to avoid aggressive rate-limiting.
#sign with letsencrypt-staging
sign with letsencrypt
}
The above example configures acme-client to obtain a TLS certificate with an
EDRSA key for the example.com
domain with the www.example.com
alternate
name. Provide your contact information to receive notifications about your
certificate and expiration alerts from your provider per their policies.
acme-client(8) will by default place ACME challenges in the /var/www/acme
directory, which nginx has been configured to serve. The staging server is
useful to test your setup is correct without hitting the rate limits, but it
will not issue trusted certificates.
Create the required directories:
mkdir -p /etc/acme
mkdir -p /var/www/acme
mkdir -p /etc/letsencrypt/live/example.com
You can now obtain the TLS certificates for your domain:
acme-client -v example.com
You can now find your fresh certificates in the
/etc/letsencrypt/live/example.com
directory. If the command failed, you will
need to find your misconfiguration and fix it.
NGINX with HTTPS
The third step is to configure the nginx https server.
Copy the https template site and rename it to example.com and enable it:
cp /etc/default/nginx/sites-available/https /etc/nginx/sites-available/example.com
ln -s ../sites-available/example.com /etc/nginx/sites-enabled/example.com
Edit the /etc/nginx/sites-available/example.com
configuration file
appropriately for your web server per the nginx documentation:
server {
server_name example.com www.example.com;
listen 443 ssl;
http2 on;
gzip off;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
include ssl-config-mozilla-intermediate;
#include ssl-config-mozilla-modern;
charset UTF-8;
add_header Strict-Transport-Security "max-age=63072000";
#add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload";
root /var/www/example.com;
location / {
index index.html;
}
location ~ \.php$ {
fastcgi_pass unix:/var/run/php-fpm;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
The above example:
- Listens on port 443 with TLS and HTTP2.
- Loads the TLS certificates.
- Includes Mozilla's intermediate strength cryptography configuration as
packaged with the operating system in
/etc/default/nginx/ssl-config-mozilla-intermediate
. These settings are general purpose and work on a reasonably wide range of devices.- You can also include
ssl-config-mozilla-modern
instead for the modern strong cryptography configuration. - Alternatively you can generate your own configuration using Mozilla's ssl-config service with the latest recommendations.
- You can also include
- Enables Strict-Transport-Security which tells browsers to only connect to your
site using HTTPS for the specified period (63072000 seconds is two years).
- Optionally, you can apply it to all subdomains as well.
- Optionally, you can also allow browsers to add your domain to their preloaded list of domains that always use HTTPS. If you're ready for the commitment, you can request your domain to be preloaded on hstspreload.org when it's operational.
- Serves files from the
/var/www/example.com
directory. - Serves .php files using the
/var/run/php-fpm
FastCGI unix socket server. You should remove this location block if you're not going to use PHP.
Create the /var/www/example.com
directory:
mkdir -p /var/www/example.com
nginx must be reloaded or restarted to serve HTTPS traffic using the certificate:
service nginx reload
You should now be able to securely connect to your webserver:
curl https://example.com
You can verify everything is set up correctly by checking your score on Qualys' SSL Test. The intermediate configuration presented here should yield a A+ grade at the time of writing. The modern configuration will only give a A grade the time of writing, since it is somewhat less compatible with common clients.
Certificate Renewal
The fourth step is to automatically renew the certificate before it expires 90 days later. acme-client will renew the certificate when invoked if it expires in 30 days or less.
Create a certificate renewal
init(5)
daemon by creating /etc/init/local-cert-example.com
:
require network optional
require nginx optional
exec sh -c 'while true; do acme-client -v example.com && service nginx reload; if [ -n "$READYFD" ]; then eval "echo >&$READYFD"; unset READYFD; fi; sleep 24h; done'
This daemon will renew the certificate on boot if needed, reload nginx if successful, and repeat every 24 hours. Once the certificate has been renewed, the daemon will become ready, allowing other deamons to depend on valid certificates being in use.
Enable your new local-cert-example.com
daemon:
service local-cert-example.com enable
You can find the renewal logs in /var/log/local-cert-example.com.log
.
PHP
Finally PHP can be enabled by simply enabling the php-fpm(8) daemon:
service php-fpm enable
nginx was configured above to talk to the /var/run/php-fpm
FastCGI unix socket
server, which php-fpm listens on by default.
Depending on your setup, you may wish to set up permissions and ownership of
some of the website files to the _php_fpm
user and group depending on your
setup. nginx will run as the _nginx
user and will also need access to serve
the files.
chown -R _php_fpm:_php_fpm /var/www/example.com
Create a PHP test page /var/www/example.com/index.php
:
<?php phpinfo(); ?>
Test whether PHP is working:
curl https://example.com/index.php
If successful, you can now start installing PHP web applications of your choice.
Follow up blog posts will walk you through installing mediawiki, phpbb, and more on Sortix. Mediawiki has been successfully installed and works, although the email registration has not been turned on and tested yet. phbBB can be installed and works, although the the user registration crashes because a DNS lookup function is missing in the standard library. Stay tuned as we bring all of these web applications online on Sortix in the near future.
Congratulations on your new Sortix web server!