@@ 7,7 7,7 @@ description: 'How to build the LinkTaco (links) project'
Requirements:
-- Go 1.20
+- Go 1.21
- GNU Make
- PostgreSQL (any modern version should work)
@@ 0,0 1,331 @@
+---
+title: 'How to self-host the LinkTaco (links) project'
+description: 'How to self-host the LinkTaco (links) project'
+---
+
+# How to self-host the LinkTaco (links) project
+
+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](build.md) 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