We don't have a Docker file and probably will never provide one. If you want to create one, please do and share it with the community. We're happy to link to it.
To self host we're going to assume that you have access to a server and at least 4 IP addresses you can use. Why 4 IP's? Because since each service can run for user's custom domains they each need to have their own IP address. The 4th IP is for the GraphQL server. This can share an IP with other hosts on the same system if there are others.
Requirements:
Notes:
We will create a links
user and then build and run this software as this
user.
root ~ # adduser
Username: links
Full name: LinkTaco.com
Uid (Leave empty for default):
Login group [links]:
Login group is links. Invite links into other groups? []:
Login class [default]:
Shell (sh csh tcsh bash rbash git-shell nologin) [sh]: bash
Home directory [/home/links]:
Home directory permissions (Leave empty for default):
Use password-based authentication? [yes]:
Use an empty password? (yes/no) [no]:
Use a random password? (yes/no) [no]: yes
Lock out the account after creation? [no]:
Username : links
Password : <random>
Full Name : LinkTaco.com
Uid : 1004
Class :
Groups : links
Home : /home/links
Home Mode :
Shell : /usr/local/bin/bash
Locked : no
OK? (yes/no): yes
adduser: INFO: Successfully added (links) to the user database.
adduser: INFO: Password for (links) is: XXXXXXXXXXXX
Add another user? (yes/no): no
Goodbye!
root ~ #
Now let's create a new PostgreSQL user and database for the application.
root ~ # createuser -U postgres -W links
Password:
root ~ # createdb -U postgres -O links links
root ~ #
Great! Now we've got a new user. Let's go into this users home directory and clone the repo.
root ~ # su - links
links ~ $ git clone https://git.code.netlandish.com/~netlandish/links
links ~ $ cd links
links ~/links $ make all links-admin links-domains
We won't go into the build steps here. Please see the Build documentation for this step.
We'll first setup the nginx configuration because it's pretty straight forward and most people are familiar with it's syntax.
In this case, we have the following in our `/usr/local/etc/nginx/nginx.conf':
http {
...
...
include /usr/local/etc/nginx/Includes/*.conf;
}
This allows us to place links-api.conf
in the /usr/local/etc/nginx/Includes
directory (create it if it doesn't exist) and we can put the api service config
in this file.
Now create the /usr/local/etc/nginx/Includes/links-api.conf
file and put the
following (obviously, change the relevant pieces):
server {
listen 123.456.789.100:443 ssl http2;
server_name api.YOURDOMAIN.com;
ssl_certificate /etc/letsencrypt/live/api.YOURDOMAIN.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.YOURDOMAIN.com/privkey.pem;
ssl_protocols TLSv1.2; # don't use SSLv3 ref: POODLE
ssl_ciphers "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECD
HE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-
AES256-SHA384";
ssl_ecdh_curve X25519:prime256v1:secp521r1:secp384r1;
# openssl dhparam -dsaparam -out /usr/local/etc/nginx/ssl/dhparams_4096.pem 4096
ssl_dhparam /usr/local/etc/nginx/ssl/dhparams_4096.pem;
ssl_prefer_server_ciphers on;
gzip off;
add_header Strict-Transport-Security "max-age=63072000; includeSubdomains" always;
#add_header Content-Security-Policy "default-src 'none'; script-src 'self'; connect-src 'self'; img-src 'self'; style-src 'self';" always;
#add_header Referrer-Policy "no-referrer";
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Feature-Policy "geolocation none; midi none; notifications none; push none; sync-xhr none; microphone none; camera none; magnetometer none; gyroscope none; speaker none; vibrate none; fullscreen self; payment none; usb none;";
add_header Allow "GET, POST, HEAD" always;
if ($request_method !~ ^(GET|POST|HEAD)$) {
return 405;
}
location / {
# Run through api daemon, Change port to whatever you have the
# api service configured to run on
proxy_pass http://127.0.0.1:5001;
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;
}
}
Note: The nginx config above assumes the existence of a LetsEncrypt SSL cert for your api domain. Please make sure the certificate exists at the path you use in the config snippet above. Obviously you can change it to meet whatever path you have on your system.
While links software itself supports auto TLS generation in this case we will
use Caddy to be the front for each of the other 3 services. So let's edit
the /usr/local/etc/caddy/Caddyfile
:
{
# This IP:Port should be where the "links-domains" service is configured to run
# locally. Generally this is localhost but your situation will vary
on_demand_tls {
ask http://localhost:5004/_check/domain
}
}
# links service
https:// {
bind 123.456.789.101
tls {
on_demand
}
reverse_proxy localhost:5000
log {
output file /var/log/caddy/links-access.log
format console
}
}
# short service
https:// {
bind 123.456.789.102
tls {
on_demand
}
reverse_proxy localhost:5002
log {
output file /var/log/caddy/short-access.log
format console
}
}
# list service
https:// {
bind 123.456.789.103
tls {
on_demand
}
reverse_proxy localhost:5003
log {
output file /var/log/caddy/list-access.log
format console
}
}
Finally let's setup the supervisord services. Edit your
/usr/local/etc/supervisord.conf
file and place the following below in the
[include]
section of the file:
files = supervisord/*.conf
If the /usr/local/etc/supervisord
directory does not yet exist then create
it. Now let's create the /usr/local/etc/supervisord/links.conf
file and add
the following:
[program:links]
command = /home/links/links/links
directory = /home/links/links
user = links
priority = 1
stopwaitsecs = 60
stopsignal = QUIT
stdout_logfile = /var/log/links.log
stderr_logfile = /var/log/links.log
[program:links-api]
command = /home/links/links/links-api
directory = /home/links/links
user = links
priority = 1
stopwaitsecs = 60
stopsignal = QUIT
stdout_logfile = /var/log/links_api.log
stderr_logfile = /var/log/links_api.log
[program:links-short]
command = /home/links/links/links-short
directory = /home/links/links
user = links
priority = 1
stopwaitsecs = 60
stopsignal = QUIT
stdout_logfile = /var/log/links_short.log
stderr_logfile = /var/log/links_short.log
[program:links-list]
command = /home/links/links/links-list
directory = /home/links/links
user = links
priority = 1
stopwaitsecs = 60
stopsignal = QUIT
stdout_logfile = /var/log/links_list.log
stderr_logfile = /var/log/links_list.log
[program:links-domains]
command = /home/links/links/links-domains
directory = /home/links/links
user = links
priority = 1
stopwaitsecs = 60
stopsignal = QUIT
stdout_logfile = /var/log/links_domains.log
stderr_logfile = /var/log/links_domains.log
Now we should be ready to restart (or start) all the services. You can run the following:
root ~ # service nginx restart
nginx: the configuration file /usr/local/etc/nginx/nginx.conf syntax is ok
nginx: configuration file /usr/local/etc/nginx/nginx.conf test is successful
Stopping nginx.
nginx: the configuration file /usr/local/etc/nginx/nginx.conf syntax is ok
nginx: configuration file /usr/local/etc/nginx/nginx.conf test is successful
Starting nginx.
root ~ # service caddy restart
Stopping caddy... done
Starting caddy... done
Log: /var/log/caddy/caddy.log
root ~ # supervisorctl udpate
adding new process: links
links: started
adding new process: links-api
links-api: started
adding new process: links-short
links-short: started
adding new process: links-list
links-list: started
adding new process: links-domains
links-domains: started
root ~ #
Note: The supervisorctl update
command is just to let the daemon know there
are new commands configured. In the future, to restart a running supervisord
service, you would use the restart
command. For example: supervisorctl restart links-api
. See the supervisord docs for more info.
You should be all setup and running now. Try loading your root domain in a browser. It may have a slight delay as the SSL certificate is generated
commit 2aac1348afc29af64b6d24324bd435bffb2881d1 Author: Peter Sanchez <peter@netlandish.com> Date: 2025-02-24T14:03:34-06:00 Fixing anchor name in support doc