Troubleshooters.Com®, Linux Library and Tinc Tips Present:

Tinc Peer to Peer Hello World


Who This Document Is Meant For

This 4500 word document could have been cut down to 1000 words if it were intended only for people 1) intimately familiar with Linux commands, 2) careful enough to avoid misreading parts of the doc and/or making transcription errors, and 3) accurately knowledgeable about VPNs in general. This doc wasn't intended for such people, although people of this category can get value out of this doc by skimming this doc instead of following it step by step, substep by substep.

On the other end of the spectrum, this document isn't useful for people who do not, cannot and will not use the command line or edit a file. In other words, a person who uses Linux just the way most people use Windows. Unless he or she is willing to temporarily suspend his or her policy against working at the command line and/or editing files, such a person must either call in a favor from friends more knowledgeable, or pay somebody, to get this Hello World tinc peer to peer VPN set up.

This document is intended for those between the preceding two extremes. Every care has been taken to eliminate ambiguity. No knowledge other than basic Linux knowledge is assumed. This document is exactly as long as it needs to be in order to maximize likelihood of constructing a working peer to peer tinc VPN, for a person between the two extremes.

When creating this Hello World peer to peer tinc VPN, I suggest you read twice and type once, to reduce the likelihood of your doing something different from what's written. Also, when you type, check for transcription errors and typos. The going will seem slow, but it's likely you'll get this setup running with few or no errors. If you see what you believe is an error in this document, please Email Steve Litt with a description of the documentation error.

Key Realizations

This section gives you several key realizations necessary to work with tinc. The person who tries to tinc without these key realizations is dead meat. The following is a list of those key realizations, each with a link to a subsection in this section.

VPNs are Tough

This "Hello World" is nowhere as simple as, let's say, the K&R C language Hello World. This is because, by their nature, VPNs are difficult, with lots of moving parts, all of which must work together before you can get anything to happen. Like all VPNs, working with tinc means keeping lots of balls in the air, and remembering not to forget anything. It's tough.

This tinc peer to peer Hello World is the simplest I can make and still demonstrate a result.

The Network Name

Many tincd commands require a "network name", and use that "network name" to identify lots of things and locate configuration. The network name is simply any old name you dream up. It can be the same as other names on your network (except for your network devices) without creating conflicts, and it can be different from everything else on your computer and will still be perfectly functional. Don't put whitespace in your network names though.

The network name is used in the following contexts:

  1. As an argument to tincd's -n option.
  2. The name of the VPN "device" (shows up in ip link command).
  3. Final directory on path to this VPN network's config path.
  4. Name of this computer's conf file inside the hosts directory.

Tinc Creates a Network Device in Software

Tinc creates a new network device, visible with the ip link, ip addr, and ifconfig commands. When you see that device in one of these commands, you know you're making progress.

You Access Your VPN With an IP Address

Everyone forgets to tell you this: I guess they think it's obvious, but it wasn't obvious to me. You access your VPN with an IP address.

For instance, if your desktop and laptop, normally operate in the LAN subnet, and both have functioning tinc servers, and the laptop's tinc assigns as the desktop's tinc subnet address, then you can see the desktop's http server with the following command:


From your laptop you can can view your desktop's NFS shares with the following command:

showmounts -e

All traffic in and out of is encrypted.

Tinc Creates Its Own Subnet

Tinc creates its own subnet to house all its VPNs. This subnet exists because of tinc, regardless of the rest of your LAN setup. You don't need to tamper with your computer's networking setup to obtain this new subnet: Tinc does it for you.

A computer's tinc can actually have multiple subnets, but that's beyond the scope of this Hello World.

Know the Tinc File Layout

       mydesk/   (substitute the network name you used in tinc -n)

The preceding structure represents a structure on a computer I chose to name mydesk, with a VPN connection to another computer I chose to name littlap (it's my laptop). Note the following:

Subnet Means Subnet!

Here's my host conf file from mydesk (/etc/tinc/mydesk/hosts/mydesk):


Address =
Subnet =

Notice very hard the fact that the subnet identifies a subnet, not the tinc IP address of mydesk. This cannot be emphasized enough. The IP address of mydesk will later be identified in the tinc-up script, but in the files within /etc/tinc/<network_name>/hosts, it identifies the subnet. The reason is that tinc uses this subnet information to automatically provide routing.

If you were to use, you'd get an error saying your IP and netmask don't match, and tincd would abort. If you were to use, tincd would appear to run OK, but you'd have no routing, and receive all sorts of "Destination host unreachable" warnings due to lack of routing.

Error Message Hierarchy

Consider the following three error messages:

  1. Destination Host Unknown
  2. Destination Host Unreachable
  3. Request timed out

#1 is the Big Kahuna: Nothing is right. The destination address seems not to exist at all. #2 means some things are right: The IP address or domain name is recognized to exist, there's just no route to it. This is probably a routing problem. #3 timed out before a response could be gotten. Check DNS. Check your firewall.

Mode = switch is Easier For Beginners

This is a Hello World type of exercise. It's supposed to be as easy as possible. Within your /etc/tinc/<network_name>/tinc.conf, be sure to specify Mode - switch, which is not the default. If you later have reason to change it to Mode - router, which is the default, you can do that later.

Tinc is a Daemon

Tinc is a daemon that runs all the time. One daemon can handle multiple subnets and multiple peers, but that's beyond the scope of this Hello World. The tinc daemon is an executable called tincd, which can background itself for init systems like sysvinit or Epoch, but can stay in the foreground by using its -D option, for inits like s6, runit, daemontools, and systemd.

I use the process supervisor on my runit init system to kill it, assuming it was run from runit in the first place. I'm pretty sure the process supervisors or managers on other inits can do this too.

Ctrl+C Doesn't Stop Tinc

By design, tincd responds to a Ctrl+C (SIGINT signal) by toggling back and forth to debug level 5, so it's hard to kill. Contrary to various misinformation on the web, you cannot terminate modern tincd versions with tinc -k. Instead, you would perform a kill -s QUIT on tincd's current process ID. You could also perform kill -9 on tincd's current process ID, but that's harsh and invites problems.

Overview of the Tinc Peer to Peer Construction Process

This article is a two computer peer to peer tinc VPN. It's pretty much symmetrical, except that each computer has a different IP address, a different tinc address, and a different network name.

  1. Install Tinc
  2. Find the Documentation
  3. Open Port 655 on Both Computers' Firewalls
  4. Preliminary Testing
  5. Make a Chart of All Properties For Both Computers
  6. Create Config Directory Structure on Both Computers
  7. Make Keys on Each Computer
  8. Create tinc.conf on Each Computers
  9. Create Hosts Files For Both Computers, Then Trade
  10. Create tinc-up on Both Computers
  11. Test In Termina on Both Computers
  12. Integrate With Init System or Process Supervisor
  13. Test Init Integration

Install Tinc

Many distributions have packages for tinc. If your distro has a reasonably up to date tinc package, use that.

Otherwise, you can download the source from My experience was that it compiles fairly easily, except that when you perform the ./configure program, it errors out every place you're missing needed files. For me, it errored out for lack of C header files, so I had to install a "devel" package every time it errored out. The ./configure step probably took me about 15 minutes, including installs.

Default Compile From Scratch

Obviously, you can skip this subsection if your distribution gives you a working tinc package.

The Void Linux x64 distro I use has a tinc package that installs and performs flawlessly. For the purpose of writing this document, I uninstalled the Void tinc package and self-compiled from scratch. The default self compile, which happens if you give the ./configure command no arguments, produces the following directory tree:


Upon completion of the whole setup process, including creation of keys and creation of scripts and config files, you'll have added the following to the tree:

  1. Be or become a normal user
  2. Get into an unimportant, throwaway directory owned by that user. Create the directory if necessary.
  3. Copy the tarball into this unimportant directory. For the sake of this document, call the tarball tinc-1.0.35.tar.gz. This document was written in May, 2019. Obviously that version number will change over time.
  4. tar xzvf tinc-1.0.35.tar.gz
  5. cd tinc-1.0.35
  6. less README in order to read any interesting facts or changes.
  7. less COPYING and less COPYING.README to view all licensing info, including a special exception for the LZO library, and special ./configure options that require GPLv3.
  8. less INSTALL in order to find any install idiocyncracies, and to get an idea of dependencies.
  9. ./configure
  10. Every time it errors out because of an uninstalled dependency, install the dependency, and retry the preceding step. Notice that many such dependencies will be the .h files for a package already installed: You'll need to also install the -devel or whatever packages for that package. Keep doing the preceding step and this one until you get a clean ./configure run with no errors (warnings are OK, but at least look at them).
  11. make
  12. sudo make install or su -c make install or su then make install
  13. su -
  14. cd /usr/local
  15. find . -mtime -2 -exec ls -ldF {} +  and make sure the expected tinc files are in the /usr/local/sbin and /usr/local/share/man directories. If they are, your initial installation is complete.

Once your install is complete, it's time to review some documentation...

Find the Documentation

If you installed from source, your documentation is in a tree under the doc directory under tinc's compile directory. For instance, the docs I got by hand-compiling are in:


The docs in the preceding directory are voluminous, including not only man pages, but the compilable .tex version of the tinc manual, and a directory full of example files.

My "real" tinc installation was created via Void Linux packaging. The only documentation included are these two man pages:

If the documents on your computer aren't sufficient, you can find a great collection of up-to-date documents at the tinc documentation page, and examples at

You'll need to do the usual stuff to incorporate those so man tincd and man tinc.conf.

Open Port 655 on Both Computers' Firewalls

By default, the tinc VPN operates on port 655, so just use that port unless you have an overpowering reason to do otherwise. Whatever your firewall, make sure it can pass both UDP and TCP packets, both inbound and outbound, for port 655.

To prove that port 655 is really open, execute the following command as root or as a normal user, and substitute this computer's IP address for IPADDR:

nmap -T4 -p655 IPADDR

Do this step on both computers, and then you can move on.

Make a Chart of All Properties For Both Computers

Before beginning to configure anything, make yourself a chart showing each of the two computers' hostname, tinc network name, real IP address, tinc IP address, and tinc subnet. The tinc subnets of the two computers should be identical, but everything else must differ to distinguish the two computers.

Making this chart first minimizes the chances of your making silly mistakes. Its intent is to make setup a secretarial task, rather than something that requires much thought. Following this paragraph is an example chart with computers named "mydesk" and "littlap". Change it to meet your own needs.

Property Computer 1 Computer 2 Notes
Hostname mydesk littlap  
Network Name mydesk littlap Can, but don't have to match hostnames**
Real IP Address  
Tinc IP Addr 4th quadrant correspondence with real IPs not necessary***
Tinc Subnet Subnet size needn't match subnet length of real IPs****
** Network name can be any string composed of [a-z], [A-Z], [0-9], and underscore [_]. If absolutely necessary, you can also use hyphens and periods [.-]. I spoze theoretically spaces could be legal, but don't be that guy who puts spaces in identifiers and causes shellscripts to break. Note, however, that the two computers cannot have identical network names.
*** In this case I set the fourth octet of the tinc IP address to the same 2 and 239 as the real IP address. Matching the fourth octets can be convenient to remember, but it's in no way necessary.
**** Just because the real IP addresses are part of a /24 network doesn't mean that the tinc subnet must be a /24. The tinc subnet can be longer (like /16) or shorter (like /28). However, the tinc subnet, and therefore the tinc IP addresses, must be in the private addressing space and therefore start with either 10., 172.16, or 192.168.

Create Special Environment Variables on Both Computers


In this section I set the following environment variables:

Variable Value
$NETNAME mydesk
$TINCCONFDIR /usr/local/etc/tinc/$NETNAME
in other words:

This section describes how and why they're set that way.

In order to make this process somewhat generic, you must set and export two environment variables: $NETNAME and $TINCCONFDIR. $NETNAME is simply the network name for this computer, from the chart you made earlier. In this document I've set $NETNAME to mydesk, so I can easily tech edit the document.

The value you set $TINCCONFDIR is contained in the error message thrown by the following command:

tincd -D -m $NETNAME

The directory within the error message thrown by the preceding command is extracted by code in the ~/ script, which is described after this paragraph.

As user root create the following ~/

export NETNAME=mydesk


### Comment out the following lines once
### you have TINCCONFDIR above set
echo -n "set TINCCONFDIR to "
2>&1 tincd -D -n $NETNAME | \
  head -n1 | \
  sed -e"s+[^/]*++;s+[^/]*$++"

In the preceding, remember to substitute your computer's network name, as shown on the chart you made, for "mydesk". Now perform the following command and note the feedback:

[root@mydesk etc]# . ~/ 
set TINCCONFDIR to /usr/local/etc/tinc/mydesk/
[root@mydesk etc]#

Notice the preceding command starts with a dot, then a space, then the name of the shellscript. What the dot does is make sure any environment variables changed in the script are changed in the current environment. I could explain the theory that makes this happen, but it's beyond the scope of this document.

Notice also that the preceding command feeds back the directory to assign to $TINCCONFDIR.

Once you've run the preceding command, perform the following command:


The preceding command echos your network name, and tinc config directory for that network name, if everything has gone right.

Finding and Setting $TINCCONFDIR

Run your command again:

[root@mydesk etc]# . ~/ 
set TINCCONFDIR to /usr/local/etc/tinc/mydesk/
[root@mydesk etc]#

Now edit ~/, uncomment the TINCCONFDIR= line, and place the path output from the command, to the right of the ~/ line's equal sign. Do not include the trailing backslash, or these instructions will fail!

Finally, now that the correct directory is in ~/ script, you can comment out all the lines below the TINCCONFDIR= line.

After I made the alterations to my ~/, it was as follows:

export NETNAME=mydesk

export TINCCONFDIR=/usr/local/etc/tinc/mydesk

### Comment out the following lines once
### you have set TINCCONFDIR set above
#echo -n "set TINCCONFDIR to "
#2>&1 tincd -D -n $NETNAME | \
#  head -n1 | \
#  sed -e"s+[^/]*++;s+[^/]*$++"

Once again, notice that the value of $TINCCONFDIR does not end in a slash. If it did, the rest of this procedure would error out!

Once your $TINCCONFDIR is configured correctly, the rest of this procedure becomes pretty easy.

Create Config Directory Structure on Both Computers

For each computer, perform the following three commands, shown as follows with their output on my computer:

[root@littlapd etc]# . ~/
[root@littlapd etc]# mkdir -p $TINCCONFDIR/hosts
[root@littlapd etc]# ls -dF $TINCCONFDIR/*
[root@littlapd etc]#

If the final command in the preceding command sequence shows a directory including "tinc", your network name from your chart, and "hosts" you've completed this section successfully.

Make Keys on Each Computer

First, perform the following:

. ~/

Be sure you've completed the Create Config Directory Structure on Both Computers before making the keys, so that there's a place for the keys to go. Once the directory structure has been made on both computers, run the following command on each computer:

tincd -K -n $NETNAME

The preceding command creates both private and public keys. For the purpose of this Hello World, just hit Enter to all the defaults. The public keys are in the host config files, to which will be added key-value pairs later. During the keymaking process, when queried for filenames, choose the defaults. To check your work, use a directory command as follows:

[root@littlapd etc]# ls -dF $TINCCONFDIR/*
[root@littlapd etc]# 

If the rsa_key.priv and files appear, you're done with this section and continue. Now that you've created keys on both computers, you're well on your way to private encrypted communication.

Create tinc.conf on Each Computers

The following is my $TINCCONFDIR/tinc.conf file from my computer 1, whose network name is mydesk:

Hostnames = no
AddressFamily = ipv4
Name = mydesk
Mode = switch
ConnectTo = littlap

And the following is my /etc/tinc/littlap/tinc.conf file from my computer 2, whose network name is littlap:

Hostnames = no
AddressFamily = ipv4
Name = littlap
Mode = switch
ConnectTo = mydesk

They're pretty much the same except that Name and ConnectTo are interchanged. The Hostnames=no entry is currently the default, and is used to prevent DNS lookup, which might be very slow.

The AddressFamily=ipv4 tells tinc that we're only dealing with IPV4 addressing. IPV6 is perfectly possible, but for simplicity is eliminated from this Hello World.

The Name entry tells the network name, for this computer, as listed on your chart.

The Mode=switch makes your life easier for this Hello World, by allowing tinc to do all the routing without work on your part.

The ConnectTo entry tells this computer to automatically connect to the computer named in this entry, upon startup of the tincd daemon. Without this entry, this computer could only wait to be connected to by the other computer. If neither had this entry, no connection would ever be made.

Create Hosts Files For Both Computers, Then Trade

As mentioned earlier, on each computer, the making of your keys in a previous step put the public and private keys, in PEM format, into files in $TINCCONFDIR.

Now it's time to create a file, named $NETNAME, in directory $TINCCONFDIR/hosts. Start with the following three commands:

. ~/
cat ../ > $NETNAME

You need to add some key-value pairs to /etc/tinc/$NETNAME/hosts/$NETNAME. Specifically, you need to add the address = and the subnet = key-value pairs.

key     value     mydesk val
address real address for this computer from your chart
subnet subnet for this computer from your chart

Use an editor to add the proper key-value pairs to the file. The following is my /etc/tinc/$NETNAME/hosts/$NETNAME file from my computer 1 whose network name is mydesk:


Address =
Subnet =

And the following is my /etc/tinc/$NETNAME/hosts/$NETNAME file from my computer 2 whose network name is littlap:


Address =
Subnet =

In both cases, the Address entry specifies the real address, for this computer, from your chart. The Subnet entry specifies the subnet, for this computer, from your chart. These two entries help tincd create all necessary routings. Please remember that the Subnet entry represents a subnet, not an individual machine's IP address. The IP address will be set later in a different file.

Once each computer has its own /etc/tinc/$NETNAME/hosts/$NETNAME, the final step in this section is to trade hosts files, so that each computer has the same two files in its /etc/tinc/NETNAME/hosts/NETNAME directory.

So in my case, I just network copy /etc/tinc/mydesk/hosts/mydesk to /etc/tinc/littlip/hosts/mydesk, and network copy /etc/tinc/littlip/hosts/littlap to /etc/tinc/mydesk/hosts/littlap.

Create tinc-up on Both Computers

Each computer has its own /etc/tinc/NETNAME/tinc-up script. This script runs every time the tincd daemon starts. This script's main responsibility, at least in this HELLO WORLD, is to give the computer a tinc IP address. It's a shellscript, so it must be permissioned executable. The following tinc-up is from my computer 1, mydesk:

ip addr add dev mydesk
ip link set dev mydesk up

And the following is the tinc-up from my computer 2, littlap:

ip addr add dev littlap
ip link set dev littlap up


Notice in the tinc-up scripts, you use the actual network name (mydesk or littlap, in my case), and not $NETNAME. The shellscript that created $NETNAME will be deleted as soon as the system is operational and stable.

So each computer's tinc-up script gives its tinc-created software network device an IP address, and brings it up.


Be absolutely sure to give each of these tinc-up scripts execute permissions, or they won't work.

Test In Terminal on Both Computers

First, execute the following command:

. ~/

Now, when you run the tincd -D -n $NETNAME command, the result you're hoping for is the following:

[root@mydesk mydesk]# tincd -D -n $NETNAME
tincd 1.0.35 starting, debug level 0
/dev/net/tun is a Linux tun/tap device (tap mode)

If instead, you get an error message that it couldn't open the pid file, then create the directory that the error message says the pid file belongs in.

In the preceding command you hope that 1) The command sits ready and does not come back to the command prompt, and 2) there are no error messages. The sitting ready without error messages doesn't mean everything's correct, but it sure is a good sign.


You cannot stop a sitting and waiting tincd by pressing Ctrl+C. Instead you must use the kill command to send its PID a SIGQUIT from another terminal to get it to stop. Once you have tinc integrated with the init system, your init system can bring it up or down without your using the kill command.

The purpose of this testing is to test tinc in the absence of init system integration. Integrating any daemon into an init system is tricky, so it's better to know it works without the init system.

Perform the following on each computer:

  1. Open a terminal and log in as user root
  2. . ~/
  3. tincd -D -n $NETNAME
  4. Handle any error messages
  5. ip link | grep $NETNAME
    • If the command yields no output, you have an odd problem beyond the scope of this document. Use resources like Google and the website and the Universal Troubleshooting Process to figure it out.
  6. Perform the following command and look for similar results:
    [root@mydesk ~]# ip addr | grep -A20 $NETNAME | grep "inet "
        inet scope global mydesk
        inet brd scope global noprefixroute mydesk
    [root@mydesk ~]#
    • If the command lists an IP address in your tinc subnet, that's a good sign. If not, your tinc-up script is probably not doing its job.
  7. Telnet into the tinc IP address of the other computer on port 22, which most people have open and supporting SSH. If you see text identifying itself as OpenSSH, you know your VPN passes information. If it fails, perhaps the other machine has a problem (not tinc configured, not on, no ssh server enabled, etc). Investigate.

When you reach the point where the daemon doesn't return to the command prompt and doesn't throw an error, and you can telnet into port 22, you can be pretty certain things are working reasonably well.

One more thing. Just to make sure that the tinc subnet ip address you tested really goes to the other computer, ssh to it just the same as you would to the computer's real address. Run the hostname command to confirm you're on the other computer.

Handle any error messages

You can skip this subsesction unless tincd threw error messages.

This subsection tells what to do for various error messages appearing after you perform the following command, where NETNAME is the computer's network name from the chart:

. ~/
tincd -D -n $NETNAME

Integrate With Init System or Process Supervisor

Either you got your tinc from your distro's package, or you downloaded source and built it. In the former case, your distro should have prepared its init to run tinc. You simply need to run a couple commands to enable it at boot and turn it on right now.

If you got your tinc as a package but life isn't as easy as the preceding paragraph implies, treat it like you compiled from scratch...

When you build tinc from source, it creates directory systemd under the compile area. Unfortunately, this directory has no systemd Unit File for tinc, at least on my system. Perhaps this is because I init with runit, not systemd. You can find text for an example systemd file here. I can't answer any further systemd questions because I'm not a systemd expert.

I'm not a sysvinit expert either so if you're initting with sysvinit, I can't help you except to advise you to set up runit under sysvinit (see, and for source code or Your distro might have an up to date (2.1.2 as of 5/19/2019) package, but not all runit packages work right.

If you're using runit, the following run script works:

exec tincd -D -n <NETNAME> --logfile

In the preceding, <NETNAME> represents your network name off your chart, and the --logfile redirects tinc's log to /var/log/tinc.<NETNAME>.log; /var/log/tinc.mydesk.log on my particular setup.

Test Init Integration

Whether you're using runit as a full init, or as a process supervisor over the sysvinit init system, the following four commands, performed as root, are what you need:

When you can reliably turn tinc on and off with your init system, you're done.


The reason <NETNAME> was used in the preceding commands instead of $NETNAME is because by the time we integrate tincd into the init system, we're not using environment variables anymore, so in the preceding commands we use <NETNAME> to represent where you put the network name from your chart.


So that's it. You created and used a Virtual Private Network (VPN). Specifically, you used it between two peer computers. You installed, created config files, figured out where the config files belonged and placed them there. All this on two computers. Then you traded the two hosts files between the two computers, tested the connectivity through the VPN, and finally integrated the tincd VPN daemon with the init system on each computer, and tested that.

This was just one use case of tinc VPNs, and not the most frequent or compelling use. But it was easy. Rest assured: Now, when you set up more useful VPN use cases, the knowledge you gained in this Hello World exercise will make doing so a lot easier.

[ Training | Troubleshooters.Com | Email Steve Litt ]