How to Install the Traccar GPS Tracking System on Ubuntu 20.04 LTS

Updated on November 21, 2023
How to Install the Traccar GPS Tracking System on Ubuntu 20.04 LTS header image

Introduction

Traccar is a free and open-source Global Positioning System (GPS) tracking system that is capable of:

  • Tracking various types of GPS devices in real-time,
  • Sending alerts via the web, email, and Short Message Service (SMS) when abnormal conditions are detected, and
  • Providing detailed device location history, map routes, and reports as required.

This tutorial explains how to set up a Traccar server on a Vultr Ubuntu 20.04 LTS server instance in a production environment.

Prerequisites

  • A fresh Vultr Ubuntu 20.04 LTS server instance with at least 2GB of memory. Say its public IPv4 address is 203.0.113.100.
  • A registered domain name for public access. Say it is example.com.

1. Setup DNS Records for Your Server

To ease public access, you need to set up DNS records through your domain hosting service provider, pointing domain names (an apex domain name and one or more subdomain names) to the IPv4 address of your server.

For example, if you are hosting the apex domain name example.com on Vultr, set up DNS records as follows to point example.com and www.example.com to the IPv4 address of your server in the DNS tab of the Vultr control panel:

Entry #1

  • Type: A
  • Name: [blank]
  • Data: 203.0.113.100
  • TTL (seconds): 300

Entry #2

  • Type: A
  • Name: www
  • Data: 203.0.113.100
  • TTL (seconds): 300

Entry #3

  • Type: MX
  • Name: [blank]
  • Data: example.com
  • TTL (seconds): 300
  • Priority: 10

Entry #4

  • Type: NS
  • Name: [blank]
  • Data: ns1.vultr.com
  • TTL (seconds): 300

Entry #5

  • Type: NS
  • Name: [blank]
  • Data: ns2.vultr.com
  • TTL (seconds): 300

See detailed instructions on managing DNS through Vultr in this Vultr tutorial.

2. Perform Basic Tasks on the Server Instance

Log in to your server instance as root from an SSH terminal, and then perform basic tasks as follows to harden the system.

Create a swap file:

# fallocate -l 2g /swap
# chmod 600 /swap
# mkswap /swap
# swapon /swap
# echo '/swap    none swap defaults 0 0' >> /etc/fstab
# free -m

Create a normal user named traccar with sudo privileges:

# useradd -ms /bin/bash traccar
# passwd traccar
New pasword:
Retype password:
Passwd: password updated successfully
# echo 'traccar    ALL=(ALL)   NOPASSWD: ALL'  tee -a /etc/sudoers.d/designated
# chmod 0440 /etc/sudoers.d/designated

Set default firewall rules with UFW:

# ufw default deny
# ufw allow 22
# ufw enable

Update and then restart the system:

# apt update
# apt upgrade -y
# apt autoremove -y
# shutdown -r now

Having the server instance up and running again, log in as traccar from an SSH terminal for follow-on work.

Be aware that the tasks mentioned above are for beginners only, and more security measures are at your discretion.

3. Install and Secure MariaDB 10.6

Traccar needs to work with a Database Management System (DBMS), such as MariaDB, MySQL, or PostgreSQL. Use commands listed below to install MariaDB 10.6, the current stable version of MariaDB, on the Ubuntu 20.04 LTS system:

$ sudo apt-get install software-properties-common dirmngr apt-transport-https -y
$ sudo apt-key adv --fetch-keys 'https://mariadb.org/mariadb_release_signing_key.asc'
$ sudo add-apt-repository 'deb [arch=amd64,arm64,ppc64el,s390x] https://mirrors.xtom.com/mariadb/repo/10.6/ubuntu focal main'
$ sudo apt update
$ sudo apt install mariadb-server -y

Start the MariaDB service:

$ sudo systemctl start mariadb.service
$ sudo systemctl enable mariadb.service

Secure the installation of MariaDB:

$ sudo mariadb-secure-installation

In the wizard, answer prompted questions as follows:

  • Enter current password for root (enter for none): :key_enter:
  • Switch to unix_socket authentication [Y/n] n:key_enter:
  • Change the root password? [Y/n] n:key_enter:
  • Remove anonymous users? [Y/n] y:key_enter:
  • Disallow root login remotely? [Y/n] y:key_enter:
  • Remove test database and access to it? [Y/n] y:key_enter:
  • Reload privilege tables now? [Y/n] y:key_enter:

4. Create a Database and a Database User for Traccar

Log in to the MariaDB shell as root:

$ sudo mysql -u root

In the MariaDB shell, create a database named traccar and a database user named traccar along with its password YourPassword for Traccar:

MariaDB [(none)]> CREATE DATABASE traccar CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
MariaDB [(none)]> CREATE USER 'traccar'@'localhost' IDENTIFIED BY 'YourPassword';
MariaDB [(none)]> GRANT ALL PRIVILEGES ON traccar.* TO 'traccar'@'localhost' IDENTIFIED BY 'YourPassword' WITH GRANT OPTION;
MariaDB [(none)]> FLUSH PRIVILEGES;
MariaDB [(none)]> EXIT;

5. Install Traccar 4.14

Download Traccar 4.14, the latest stable release of Traccar for now:

$ cd
$ wget https://github.com/traccar/traccar/releases/download/v4.14/traccar-linux-64-4.14.zip

Unzip the Traccar archive:

$ sudo apt install unzip -y
$ unzip traccar-linux-64-4.14.zip

Install Traccar:

$ sudo ./traccar.run

Use the database credentials (database: traccar, database user: traccar, database password: YourPassword) you setup earlier to update the Traccar configuration file:

$ cat <<EOF  sudo tee /opt/traccar/conf/traccar.xml
  <?xml version='1.0' encoding='UTF-8'?>
  
  <!DOCTYPE properties SYSTEM 'http://java.sun.com/dtd/properties.dtd'>
  
  <properties>
  
      <entry key="config.default">./conf/default.xml</entry>
  
      <entry key='database.driver'>com.mysql.jdbc.Driver</entry>
      <entry key='database.url'>jdbc:mysql://localhost/traccar?serverTimezone=UTC&amp;useSSL=false&amp;allowMultiQueries=true&amp;autoReconnect=true&amp;useUnicode=yes&amp;characterEncoding=UTF-8&amp;sessionVariables=sql_mode=''</entry>
      <entry key='database.user'>traccar</entry>
      <entry key='database.password'>YourPassword</entry>
  
  </properties>
  EOF

To avoid running the Traccar service as root, which is an unsafe operation in production, change the owner of the Traccar service as follows:

$ sudo mkdir /etc/systemd/system/traccar.service.d/
$ cat <<EOF  sudo tee /etc/systemd/system/traccar.service.d/run-as-user.conf
  [Service]
  User=traccar
  Group=traccar
  EOF

Also, change the owner of Traccar files from root to traccar:

$ sudo chown -R traccar:traccar /opt/traccar

Start the Traccar service:

$ sudo systemctl daemon-reload
$ sudo systemctl enable traccar.service
$ sudo systemctl start traccar.service

Delete Traccar installation files to save space:

$ cd
$ rm README.txt traccar*

Change firewall rules to allow access to port 8082 on which the Traccar service is listening:

$ sudo ufw allow 8082

Open more ports to allow communications between the Traccar server and various GPS client devices:

$ sudo ufw allow 5000:5300/tcp
$ sudo ufw allow 5000:5300/udp

Point your favorite web browser to http://203.0.113.100:8082 to access the Traccar server web interface, and then log in with the default credentials listed below:

  • Email: admin
  • Password: admin

For security purposes, you should change the default password after logging in.

Feel free to navigate your Traccar server website and add your GPS devices. Or, install a Traccar client on your Android or iOS smartphones to enable tracking functionality on them.

The above instructions on deploying a Traccar server on Ubuntu 20.04 LTS is enough for personal use. If your Traccar server is for public access in a production environment, move on to the next steps.

6. Install Nginx 1.20.2

Because the Traccar server listens on the port 8082 rather than the default HTTP port 80, it's desirable to set up a reverse proxy for Traccar using Nginx in production. Follow the instructions listed below to install the latest stable version of Nginx using official pre-built Nginx binary packages.

Install required components:

$ sudo apt install curl gnupg2 ca-certificates lsb-release ubuntu-keyring -y

Import the official Nginx signing key for package authentication:

$ curl https://nginx.org/keys/nginx_signing.key  gpg --dearmor \
       sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null

Verify the signing key within the downloaded file:

$ gpg --dry-run --quiet --import --import-options import-show /usr/share/keyrings/nginx-archive-keyring.gpg

The output should include this fingerprint: 573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62. If it prints a different value, remove the downloaded file /usr/share/keyrings/nginx-archive-keyring.gpg and then re-run the above importing command.

Setup the apt repository for stable Nginx packages:

$ echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
  http://nginx.org/packages/ubuntu `lsb_release -cs` nginx" \
       sudo tee /etc/apt/sources.list.d/nginx.list

Setup repository pinning to avoid installing distribution-provided Nginx packages:

$ echo -e "Package: *\nPin: origin nginx.org\nPin: release o=nginx\nPin-Priority: 900\n" \
       sudo tee /etc/apt/preferences.d/99nginx

Install the latest stable version of Nginx, which is Nginx 1.20.2 for now:

$ sudo apt update
$ sudo apt install nginx

Confirm the installation and learn about installation details:

$ nginx -V

Start the Nginx service:

$ sudo systemctl start nginx.service
$ sudo systemctl enable nginx.service

Change firewall rules to allow HTTP traffic on port 80 and HTTPS traffic on port 443:

$ sudo ufw allow 80
$ sudo ufw allow 443

Disallow direct access on port 8082:

$ sudo ufw delete allow 8082

7. Get Free Let's Encrypt TLS Certificates

In a production environment, valid TLS certificates are indispensable for enabling HTTPS access to the website. Follow the instructions listed below to get free Let's Encrypt TLS certificates for example.com and www.example.com using the Certbot Automatic Certificate Management Environment (ACME) client.

Update the pre-installed snapd program on Ubuntu 20.04 LTS:

$ sudo snap install core; sudo snap refresh core

To avoid conflicts, remove any existing Certbot programs installed using the system package manager or the certbot-auto script:

$ sudo apt-get remove certbot
$ sudo sed -i '/certbot-auto/d' /etc/crontab
$ sudo rm -f /usr/local/bin/cert-bot
$ sudo rm -rf /opt/eff.org

Use snapd to install Certbot:

$ sudo snap install --classic certbot

Create a symbolic link in the /usr/bin directory to ease the use of the certbot command:

$ sudo ln -s /snap/bin/certbot /usr/bin/certbot

Get Let's Encrypt certificates for designated domain names using the Certbot program and its Nginx plugin:

$ sudo certbot certonly --nginx

In the Certbot program, reply to prompted questions as follows to get certificates for example.com and www.example.com:

  • Enter email address: admin@example.com
  • Do you agree to the terms of service? `y'
  • Would you be willing to share your email address with the Electronic Frontier Foundation? n
  • Please enter the domain name(s) you would like on your certificate (comma or space separated): example.com www.example.com

The Certbot program has set up automatic certificate renewal by default to prevent Let's Encrypt TLS certificates from expiring in 90 days. Use commands listed below to confirm those renewals are accountable:

$ sudo certbot renew --dry-run
$ sudo systemctl list-timers  grep snap.certbot.renew.service

8. Setup Nginx as a Reverse Proxy for Traccar

Follow instructions in this section to setup Nginx as a reverse proxy for Traccar in a production environment, which is capable of:

  • Redirecting example.com and its non-www subdomains to www.example.com,
  • Redirecting HTTP to HTTPS, and
  • Disallow direct access to the server's IPv4 address 203.0.113.100.

Create directories to store Nginx server configuration files:

$ sudo mkdir /etc/nginx/sites-available
$ sudo mkdir /etc/nginx/sites-enabled

Backup preset Nginx configuration files:

$ sudo mv /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak
$ sudo mv /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.bak

To increase SSL security, generate a 2048-bit Diffie-Hellman parameters file for Nginx:

$ sudo openssl dhparam -out /etc/nginx/dhparam.pem 2048

Create the main Nginx configuration file:

$ cat <<EOF  sudo tee /etc/nginx/nginx.conf
  user                 nginx;
  pid                  /run/nginx.pid;
  worker_processes     auto;
  worker_rlimit_nofile 65535;
  
  events {
      multi_accept       on;
      worker_connections 65535;
  }
  
  http {
      charset                utf-8;
      sendfile               on;
      tcp_nopush             on;
      tcp_nodelay            on;
      server_tokens          off;
      log_not_found          off;
      types_hash_max_size    2048;
      types_hash_bucket_size 64;
      client_max_body_size   16M;
  
      # MIME
      include                mime.types;
      default_type           application/octet-stream;
  
      # Logging
      access_log             /var/log/nginx/access.log;
      error_log              /var/log/nginx/error.log warn;
  
      # Limits
      limit_req_log_level    warn;
      limit_req_zone         $binary_remote_addr zone=login:10m rate=10r/m;
  
      # SSL
      ssl_session_timeout    1d;
      ssl_session_cache      shared:SSL:10m;
      ssl_session_tickets    off;
  
      # Diffie-Hellman parameters
      ssl_dhparam            /etc/nginx/dhparam.pem;
  
      # Mozilla Intermediate configuration
      ssl_protocols          TLSv1.2 TLSv1.3;
      ssl_ciphers            ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
  
      # OCSP Stapling
      ssl_stapling           on;
      ssl_stapling_verify    on;
      resolver               1.1.1.1 1.0.0.1 8.8.8.8 8.8.4.4 208.67.222.222 208.67.220.220 valid=60s;
      resolver_timeout       2s;
  
      # Setup connection header for WebSocket reverse proxy
      map $http_upgrade $connection_upgrade {
          default upgrade;
          ""      close;
      }
  
      map $remote_addr $proxy_forwarded_elem {
  
          # IPv4
          ~^[0-9.]+$        "for=$remote_addr";
          # IPv6
          ~^[0-9A-Fa-f:.]+$ "for=\"[$remote_addr]\"";
          # Unix domain socket names
          default           "for=unknown";
      }
  
      map $http_forwarded $proxy_add_forwarded {
  
          # If the incoming Forwarded header is syntactically valid
          "~^(,[ \\t]*)*([!#$%&'*+.^_`~0-9A-Za-z-]+=([!#$%&'*+.^_`~0-9A-Za-z-]+\"([\\t \\x21\\x23-\\x5B\\x5D-\\x7E\\x80-\\xFF]\\\\[\\t \\x21-\\x7E\\x80-\\xFF])*\"))?(;([!#$%&'*+.^_`~0-9A-Za-z-]+=([!#$%&'*+.^_`~0-9A-Za-z-]+\"([\\t \\x21\\x23-\\x5B\\x5D-\\x7E\\x80-\\xFF]\\\\[\\t \\x21-\\x7E\\x80-\\xFF])*\"))?)*([ \\t]*,([ \\t]*([!#$%&'*+.^_`~0-9A-Za-z-]+=([!#$%&'*+.^_`~0-9A-Za-z-]+\"([\\t \\x21\\x23-\\x5B\\x5D-\\x7E\\x80-\\xFF]\\\\[\\t \\x21-\\x7E\\x80-\\xFF])*\"))?(;([!#$%&'*+.^_`~0-9A-Za-z-]+=([!#$%&'*+.^_`~0-9A-Za-z-]+\"([\\t \\x21\\x23-\\x5B\\x5D-\\x7E\\x80-\\xFF]\\\\[\\t \\x21-\\x7E\\x80-\\xFF])*\"))?)*)?)*$" "$http_forwarded, $proxy_forwarded_elem";
  
          # Otherwise
          default "$proxy_forwarded_elem";
      }
  
      # Load site-specific configurations
      include /etc/nginx/conf.d/*.conf;
      include /etc/nginx/sites-enabled/*;
  }
  EOF

Create the Nginx configuration file for example.com:

$ cat <<EOF  sudo tee /etc/nginx/sites-available/example.com.conf
  server {
      listen                  443 ssl http2;
      listen                  [::]:443 ssl http2;
      server_name             www.example.com;
  
      # Use Let's Encrypt SSL certificates
      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;
  
      # Add security headers
      add_header X-XSS-Protection          "1; mode=block" always;
      add_header X-Content-Type-Options    "nosniff" always;
      add_header Referrer-Policy           "no-referrer-when-downgrade" always;
      add_header Content-Security-Policy   "default-src 'self' http: https: data: blob: 'unsafe-inline' 'unsafe-eval'; frame-ancestors 'self';" always;
      add_header Permissions-Policy        "interest-cohort=()" always;
      add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
  
      # Restrict unsafe HTTP request methods
      if ($request_method !~ ^(GETPOSTHEADPUTDELETE)$) {
          return '405';
      }
  
      # Setup a reverse proxy for Traccar
      location / {
          proxy_pass http://127.0.0.1:8082;
          proxy_redirect http://127.0.0.1:8082/ http://$host:$server_port/;
          proxy_redirect ws://127.0.0.1:8082/api/socket ws://$host:$server_port/api/socket;
          proxy_http_version                 1.1;
          proxy_cache_bypass                 $http_upgrade;
  
          # Proxy headers
          proxy_set_header Upgrade           $http_upgrade;
          proxy_set_header Connection        $connection_upgrade;
          proxy_set_header Host              $host;
          proxy_set_header X-Real-IP         $remote_addr;
          proxy_set_header Forwarded         $proxy_add_forwarded;
          proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
          proxy_set_header X-Forwarded-Proto $scheme;
          proxy_set_header X-Forwarded-Host  $host;
          proxy_set_header X-Forwarded-Port  $server_port;
  
          # Proxy timeouts
          proxy_connect_timeout              60s;
          proxy_send_timeout                 60s;
          proxy_read_timeout                 60s;
      }
  
      # favicon.ico
      location = /favicon.ico {
          log_not_found off;
          access_log    off;
      }
  
      # robots.txt
      location = /robots.txt {
          log_not_found off;
          access_log    off;
      }
  
      # Enable gzip compressions
      gzip            on;
      gzip_vary       on;
      gzip_proxied    any;
      gzip_comp_level 6;
      gzip_types      text/plain text/css text/xml application/json application/javascript application/rss+xml application/atom+xml image/svg+xml;
  }
  
  # Redirect non-www subdomains to www.example.com
  server {
      listen                  443 ssl http2;
      listen                  [::]:443 ssl http2;
      server_name             .example.com;
  
      # SSL
      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;
      return                  301 https://www.example.com$request_uri;
  }
  
  # Redirect HTTP to HTTPS
  server {
      listen      80;
      listen      [::]:80;
      server_name .example.com;
      
      location / {
          return 301 https://www.example.com$request_uri;
      }
  }
  
  # Disallow direct access to the server's IPv4 and IPv6 addresses
  server {
      listen      80 default_server;
      listen      [::]:80 default_server;
      server_name "";
      return 444;
  }
  EOF

Create a symbolic link to enable the configuration file for example.com:

$ sudo ln -s /etc/nginx/sites-available/example.com.conf /etc/nginx/sites-enabled/

Test the changed Nginx configurations and then reload the Nginx service:

$ sudo nginx -t
$ sudo systemctl reload nginx.service

Now it's time to point your favorite web browser to http://example.com to verify the URL redirects you set up earlier. If all goes well, it should switch to https://www.example.com.

More Information

Learn more about Traccar: