~netlandish/links

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:

  • Root access to Server / VPS running Unix like operating system where you can install the application. Linux or BSD is fine.
  • 4 IP addresses
  • Go 1.21+
  • PostgreSQL (any modern-ish version should work) installed and running.
  • supervisord installed
  • nginx installed
  • Caddy installed
  • Some basic system admin skills to get through this process.

Notes:

  • The example commands in this document are based on the FreeBSD operating system (OS), so if you're installing to Linux or some other Unix like OS, please refer to your OS documentation for the equivalent commands or file locations.
  • This is just one way to deploy this software. We are not saying this is the best way. Please review your own network, requirements, environments, etc. to determine your best deployment strategy.
#Initial setup

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.

#nginx

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.

#Caddy

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

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
#Starting services

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.

#Done.

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

About this wiki

commit 2aac1348afc29af64b6d24324bd435bffb2881d1
Author: Peter Sanchez <peter@netlandish.com>
Date:   2025-02-24T14:03:34-06:00

Fixing anchor name in support doc
Clone this wiki
https://git.code.netlandish.com/~netlandish/links-wiki (read-only)
git@git.code.netlandish.com:~netlandish/links-wiki (read/write)