# Install Linty Platform
Linty extends the [SonarQube platform](https://www.sonarqube.org/), the leader platform in clean code. Linty adds the
following plugins:
* **VHDL**: To analyze [VHDL](https://en.wikipedia.org/wiki/VHDL) code
* **Verilog**:  To analyze [Verilog/SystemVerilog](https://en.wikipedia.org/wiki/Verilog) code
* **Tcl**: To analyse [Tcl](https://en.wikipedia.org/wiki/Tcl) (Tool Command Language) scripts
* **ModelSim/QuestaSim Code Coverage**: To decorate code from ModelSim code coverage reports
The Linty platform is made of two components:
* A web server to display quality reports
* A database (PostgreSQL) to store data
The Linty platform is provided as a Docker image. Please, follow below documentation to install and configure it.
Installation process is the same whether your have purchased [Linty Ultra](https://linty-services.com/pricing) or not.
If you already have your own SonarQube platform, download Linty plugins
from [Linty website](https://download.linty-services.com/plugins/) into `$SONARQUBE_HOME/extensions/plugins` and restart your
SonarQube platform. Then, jump directly to [Configure Linty Platform](/doc/configure.md).
## Linty Server / SonarQube Versions
|  Linty Server  | Embedded SonarQube Version
(Community Edition) |
|:--------------:|:-------------------------------------------------:|
| 4.1.1 (latest) |                      2025.3                       |
|     4.1.0      |                      2025.3                       |
|     4.0.1      |                      10.7.0                       |
|     3.1.0      |                      10.5.1                       |
|     3.0.0      |                      10.3.0                       |
|     2.1.0      |                      10.3.0                       |
|     2.0.0      |                      10.2.0                       |
|     1.2.0      |                      10.1.0                       |
|     1.1.0      |                      10.0.0                       |
|     1.0.0      |                       9.9.0                       |
## Requirements
### Hardware
* **Memory**: At least 2GB of free RAM is necessary to run the web server and the database.
* **CPU**: There will not be a heavy usage of CPU server-side. Invest more on CPU [scanner-side](scan.md#requirements).
* **Disk**:
  * Hard drives that have excellent read & write performance (SSD) are highly recommended.
  * Necessary disk space will depend on how much code you analyze. 10GB should already allow you to store data for several large projects.
### Network
No external connection is needed to run the Linty platform.
### Operating System
* Linty runs on Linux. We recommend Ubuntu. But distribution is up to you.
* [Docker Engine (with containerd and Docker Compose) 23 or greater](https://docs.docker.com/engine/install/ubuntu/) should be installed
## Deploy Linty Platform
1. Create a `linty` user:
   ```bash
   # Create new 'linty' user
   sudo adduser linty
    
   # Set 'linty' user's password if not asked during the previous step, ignore otherwise.
   sudo passwd linty
   # Add 'linty' to sudoers
   #  - Ubuntu/Debian
   sudo adduser linty sudo
   #  - CentOS
   sudo usermod -aG wheel linty
   
   # Add 'linty' to 'docker' group
   sudo groupadd docker
   sudo usermod -aG docker linty
   ```
2. All the below commands (whatever the section) are to be run with the `linty` user. So, let's become `linty` user:
   ```
   sudo su - linty
   ```
3. Create a new file: `sudo vi /etc/sysctl.d/99-linty.conf`, with the following content:
   ```properties
   vm.max_map_count=524288
   fs.file-max=131072
   ```
4. Reload kernel properties:
   ```bash
   sudo sysctl --system
   ```
5. Create a directory to store Linty artifacts: `$LINTY_HOME`. We propose `/opt/linty`. But location is up to you.
   ```bash
   sudo mkdir /opt/linty
   sudo chown linty:linty /opt/linty
   ```
6. Create a new file: `vi $LINTY_HOME/docker-compose.yml` (Replace `$LINTY_HOME` with proper location).
   Replace `` with proper `lintyservices/linty-server` image
   version. Feel free to change database name, user and password in both containers but do not forget to replace those
   values in subsequent commands as default `linty` values are used in this documentation. Double-check proper
   two-space indentation before saving the file.
   ```yaml
   services:
     linty-server:
       depends_on:
         - linty-database
       image: lintyservices/linty-server:
       container_name: linty-server
       ports:
         - 9000:9000
       networks:
         - linty-network
       environment:
         - SONAR_JDBC_URL=jdbc:postgresql://linty-database:5432/linty_db_name
         - SONAR_JDBC_USERNAME=linty_db_user
         - SONAR_JDBC_PASSWORD=linty_db_password
       volumes:
         - linty_data:/opt/sonarqube/data
         - linty_logs:/opt/sonarqube/logs
     linty-database:
       image: postgres:15
       container_name: linty-database
       networks:
         - linty-network
       environment:
         - POSTGRES_DB=linty_db_name
         - POSTGRES_USER=linty_db_user
         - POSTGRES_PASSWORD=linty_db_password
       volumes:
         - linty_database:/var/lib/postgresql
         - linty_database_data:/var/lib/postgresql/data
   volumes:
     linty_data:
     linty_logs:
     linty_database:
     linty_database_data:
   networks:
     linty-network:
   ```
7. Create and start Linty:
   ```bash
   cd $LINTY_HOME
   docker compose up -d
   ```
8. Wait for a few seconds before browsing [http://localhost:9000](http://localhost:9000). Default credentials are
   admin/admin.
9. Change `admin` password when prompted
10. Accept usage of third-party plugins when prompted
11. You can now [configure your Linty platform](configure.md) and [scan your code](scan.md)
## Manually Stop and Restart Linty
To stop Linty:
```bash
cd $LINTY_HOME
docker compose stop
```
To restart Linty:
```bash
cd $LINTY_HOME
docker compose start
```
## Run Linty as a Service
1. Create a new file: `sudo vi /etc/systemd/system/linty.service`. Replace `$LINTY_HOME` with proper location.
   ```properties
   [Unit]
   Description=Linty
   Requires=docker.service
   After=docker.service
   [Service]
   Type=simple
   User=linty
   Group=linty
   PermissionsStartOnly=true
   ExecStart=/usr/bin/docker compose -f $LINTY_HOME/docker-compose.yml up --force-recreate
   ExecStop=/usr/bin/docker compose -f $LINTY_HOME/docker-compose.yml down
   TimeoutStartSec=10
   Restart=always
   [Install]
   WantedBy=multi-user.target
   ```
2. To take into account this new `linty` service, run once:
   ```bash
   sudo systemctl daemon-reload
   ```
3. To automatically start `linty` service at boot, run once:
   ```bash
   sudo systemctl enable linty
   ```
4. To manually start / restart / stop `linty` service:
   ```bash
   sudo systemctl start linty
   sudo systemctl restart linty
   sudo systemctl stop linty
   ```
## Backup and Restore Database
Create a directory to store database backups: `$LINTY_BACKUP_DIR`. We propose `/opt/linty/backups`. But location is up
to you.
```bash
mkdir /opt/linty/backups
```
### Backup Database
#### Manual Backup
```bash
docker exec linty-database /bin/bash \
  -c "/usr/bin/pg_dump -F c -U linty_db_user linty_db_name" \
  | gzip -9 > $LINTY_BACKUP_DIR/linty-database-backup.sql.gz 
```
#### Automated Backups
1. Create a new file: `vi $LINTY_HOME/linty-database-backup.sh` (Replace `$LINTY_HOME` with proper
   location) with the following content (Replace `$LINTY_BACKUP` with proper location):
   ```bash
   #!/bin/bash
   set -e
   DEST_DIR=/opt/linty/backups
   DEST=$DEST_DIR/linty-database-backup-$(date +%Y-%m-%d-%H%M).sql
   KEEP_DAYS=15
   docker exec linty-database /bin/bash -c "/usr/bin/pg_dump -F c -U linty_db_user linty_db_name" > $DEST
   gzip -9 $DEST
   find $DEST_DIR -type f -mtime +$KEEP_DAYS -delete
   ```
2. Make this script executable:
   ```bash
   chmod +x linty-database-backup.sh 
   ```
3. Add cron job to back up your database on a regular basis:
   ```bash
   sudo crontab -e
   # For instance, add the following line to back up the database every day at 2:00 AM
   # (replace $LINTY_HOME with proper location):
   0 2 * * * sudo su linty -c '$LINTY_HOME/linty-database-backup.sh'
   ```
4. On a regular basis, copy `$LINTY_BACKUP_DIR` directory to another machine to have redundant backup
### Restore Database
1. Stop Linty:
   ```bash
   # If installed as a service:
   sudo systemctl stop linty
   # If not installed as a service:
   cd $LINTY_HOME
   docker compose stop
   ```
2. Start database container only:
   ```bash
   cd $LINTY_HOME
   docker compose up -d linty-database
   ```
3. Restore from database backup:
   ```bash
   cd $LINTY_BACKUP_DIR
   gzip -d -k linty-database-backup.sql.gz
   docker cp linty-database-backup.sql linty-database:/var/lib/postgresql/data
   docker exec linty-database psql -U linty_db_user -d postgres -c "DROP DATABASE linty_db_name;"
   docker exec linty-database psql -U linty_db_user -d postgres -c "CREATE DATABASE linty_db_name OWNER linty_db_user;"
   docker exec linty-database pg_restore -U linty_db_user -d linty_db_name /var/lib/postgresql/data/linty-database-backup.sql
   docker container stop linty-database
   docker container rm linty-server linty-database
   docker volume rm linty_linty_data
   ```
4. Restart Linty:
   ```bash
   # If installed as a service:
   sudo systemctl restart linty
   # If not installed as a service:
   cd $LINTY_HOME
   docker compose up -d
   ```
## Upgrade Linty
1. Back up your Linty database: See [Backup database section](#backup-database).
2. Carefully read the [upgrade notes](release_notes).
3. Stop Linty:
   ```bash
   # If installed as a service:
   sudo systemctl stop linty
   # If not installed as a service:
   cd $LINTY_HOME
   docker compose down
   ```
4. Update version of `lintyservices/linty-server` Docker image in `$LINTY_HOME/docker-compose.yml`:
   ```yaml
   services:
     linty-server:
       ...
       image: lintyservices/linty-server:
   ```
5. Restart Linty:
   ```bash
   # If installed as a service:
   sudo systemctl restart linty
   # If not installed as a service:
   cd $LINTY_HOME
   docker compose up --force-recreate -d
   ```
6. Browse [http://localhost:9000/setup](http://localhost:9000/setup) and update database if required
## Migrate from a Legacy (non-Docker) Linty Platform
If you are not running PostgreSQL 11 or greater, please contact Linty support before starting the migration.
1. [Upgrade your SonarQube platform to the latest LTS (9.9.0)](https://www.sonarsource.com/products/sonarqube/downloads/)
   with [latest Linty plugins](https://github.com/Linty-Services/public-share/releases/tag/latest)
2. Shut down your current SonarQube platform
3. Follow "Create Linty platform" section. Replace database name, user and password with your current values.
4. Backup your current database:
   ```bash
   /usr/bin/pg_dump -F c -U   | gzip -9 > ./linty-database-backup.sql.gz 
   ```
5. Follow "Backup and restore database > Restore database" section with the backup you just created
6. Ask for a new [license key](configure.md)
## Install Linty Documentation Locally
Linty latest version of the documentation is available [online](https://doc.linty-services.com).
Linty documentation can also be made available locally from you host machine if, for instance:
* You do not have access to the Internet
* You want to be able to access a specific version of the documentation (the one related to your Linty Server version,
  not the latest version that is available online)
To make the documentation available locally:
1. Add a `linty-doc` service to `$LINTY_HOME/docker-compose.yml`:
   ```yaml
   version: "3.8"
   services:
     linty-server:
       ...
     linty-database:
       ...
     linty-doc:
       image: lintyservices/linty-doc:  #  should match linty-server version
       container_name: linty-doc
       ports:
         - 8080:80
       networks:
         - linty-network
   volumes:
     ...
   networks:
     ...
   ```
2. Restart Linty:
   ```bash
   # If installed as a service:
   sudo systemctl restart linty
   # If not installed as a service:
   cd $LINTY_HOME
   docker compose up --force-recreate -d
   ```
3. Browse local documentation at [http://localhost:8080](http://localhost:8080)
## Clean Up Before Re-installing Linty from Scratch
To start from scratch and run a fresh Linty platform:
1. Stop Linty:
   ```bash
   # If installed as a service:
   sudo systemctl stop linty
   # If not installed as a service:
   cd $LINTY_HOME
   docker compose down
   ```
2. Clean up:
   ```bash
   # Remove existing Docker containers
   docker container rm linty-server linty-database
   # Remove existing Docker volumes
   # Volume prefix is 'linty-'. It is the name of the directory containing your docker-compose.yml file followed by an underscore.
   # Update the prefix accordingly if your docker-compose.yml file is not located in /opt/linty
   docker volume rm \
     linty_linty_data \
     linty_linty_database \
     linty_linty_database_data \
     linty_linty_logs
   ```
## Debug
To follow logs:
```bash
docker logs -f 
# To follow logs of linty-server:
docker logs -f linty-server
# To follow logs of linty-database:
docker logs -f linty-database
```
To enter a container with a bash:
```bash
docker exec -it  bash
# To debug linty-server:
docker exec -it linty-server bash
# To debug linty-database:
docker exec -it linty-database bash
```
## Add Additional Plugins
To add additional plugins:
1. Create a new directory: `mkdir /opt/linty/custom_plugins`
2. Add your custom plugins to this directory
3. Update `/opt/linty/docker-compose.yml` as below:
   ```yaml
   services:
     linty-server:
       ...
       volumes:
         - /opt/linty/custom_plugins:/opt/sonarqube/extensions/custom_plugins
         ...
   ```
## Securing the server behind a proxy (nginx)
Let's say that the URL to access the Linty server would be: `https://linty.my-domain.com`
### Install and configure nginx
Install nginx:
```
sudo apt install nginx
```
Create `linty.my-domain.com` file in `/etc/nginx/sites-available`:
```
cd /etc/nginx/sites-available
sudo vi linty.my-domain.com
```
With the following content:
```
server {
  listen 80;
  server_name linty.my-domain.com www.linty.my-domain.com;
}
```
Restart nginx:
```
sudo ln -s /etc/nginx/sites-available/linty.my-domain.com /etc/nginx/sites-enabled/
sudo systemctl restart nginx
sudo systemctl status nginx
```
### Generate SSL certificates with Let’s Encrypt
See for instance [this DigitalOcean tutorial](https://www.digitalocean.com/community/tutorials/how-to-secure-nginx-with-let-s-encrypt-on-debian-10).
```
sudo apt install python3-acme python3-certbot python3-mock python3-openssl python3-pkg-resources python3-pyparsing python3-zope.interface
sudo apt install python3-certbot-nginx
sudo certbot --nginx -d linty.my-domain.com -d www.linty.my-domain.com
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator nginx, Installer nginx
Enter email address (used for urgent renewal and security notices) (Enter 'c' to
cancel): my-email-address@my-domain.com
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf. You must
agree in order to register with the ACME server at
https://acme-v02.api.letsencrypt.org/directory
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(A)gree/(C)ancel: A
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Would you be willing to share your email address with the Electronic Frontier
Foundation, a founding partner of the Let's Encrypt project and the non-profit
organization that develops Certbot? We'd like to send you email about our work
encrypting the web, EFF news, campaigns, and ways to support digital freedom.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: N
Obtaining a new certificate
Performing the following challenges:
http-01 challenge for linty.my-domain.com
http-01 challenge for www.linty.my-domain.com
Waiting for verification...
Cleaning up challenges
Deploying Certificate to VirtualHost /etc/nginx/sites-enabled/linty.my-domain.com
Deploying Certificate to VirtualHost /etc/nginx/sites-enabled/linty.my-domain.com
Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: No redirect - Make no further changes to the webserver configuration.
2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for
new sites, or if you're confident your site works on HTTPS. You can undo this
change by editing your web server's configuration.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 2
Redirecting all traffic on port 80 to ssl in /etc/nginx/sites-enabled/linty.my-domain.com
Redirecting all traffic on port 80 to ssl in /etc/nginx/sites-enabled/linty.my-domain.com
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Congratulations! You have successfully enabled https://linty.my-domain.com
and https://www.linty.my-domain.com
```
### Update nginx configuration
```
sudo vi /etc/nginx/sites-available/linty.my-domain.com
```
With below content (see lines not commented with 'managed by Certbot'):
```
server {
    server_name linty.my-domain.com www.linty.my-domain.com;
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/linty.my-domain.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/linty.my-domain.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
    if ($host = www.linty.my-domain.com) {
        return 301 https://linty.my-domain.com$request_uri;
    }
    client_max_body_size 500M;
    location / {
        proxy_pass http://localhost:9000;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header X-Forwarded-Proto https;
    }
}
server {
    if ($host = www.linty.my-domain.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot
    if ($host = linty.my-domain.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot
    listen 80;
    server_name linty.my-domain.com www.linty.my-domain.com;
    return 404; # managed by Certbot
}
```
Restart nginx:
```
sudo systemctl restart nginx
sudo systemctl status nginx
```
In your browser, check that SonarQube web interface is displayed at https://linty.my-domain.com and that you can login.
Check that:
* `http://linty.my-domain.com/` redirects to `https://linty.my-domain.com/`
* `http://www.linty.my-domain.com` and `https://www.linty.my-domain.com` redirect to `https://linty.my-domain.com/`
See also [SonarQube documentation](https://docs.sonarsource.com/sonarqube-server/latest/setup-and-upgrade/operating-the-server/#securing-the-server-behind-a-proxy).
## Docker Image Content
* [SonarQube Community Edition](https://hub.docker.com/_/sonarqube)
* All Linty Plugins (VHDL, Verilog/SystemVerilog, Tcl, Code coverage, etc.)
* JDK 17