Creating a 24/7 “Lofi Like” stream (Liquidsoap script template | Linux VPS installation guide) – Mikulski
Site Overlay

Creating a 24/7 “Lofi Like” stream (Liquidsoap script template | Linux VPS installation guide)

Special thanks to EvergreenDeer, the author of the Deer Radio, for the advices <3

What’s in this article?

This guide contains, in fact, a ready-made script in the Liquidsoap, which plays a looped video / animation / image and a playlist with music from a folder (by adding new tracks to the folder, the playlist will be updated automatically), shows the metadata of the tracks on the screen (artist – song), and sends the stream to Youtube (or any other platform that supports streams). Simply put, a typical Lofi radio template.
As well as detailed step-by-step instructions for installing and configuring the minimum required to run on a virtual private server based on Linux (Ubuntu in a specific case). Taking into account that even a beginner could figure out and repeat this procedure.
In general, all the information that I sorely lacked when I caught fire with the idea of creating my own 24/7 music stream.
It will not do without lyrical digressions, which could be skipped, but it is also impossible without them.

UPD: There is an addition to this guide:, with examples of how you can improve your radio stream.

My coolstory

Anyway, this guide echoes my previous tutorial “How I made my Stream Radio 24/7” based on the Ffplayout script. Unfortunately, the material is no longer so relevant, because the developer switched from Python to Rust, and therefore the installation process and individual configuration details have changed. Nevertheless, in that post I described a little more detailed about the choice of VPS and other points than will be in this post, and therefore I also recommend you to familiarize yourself.
Ffplayout is designed specifically for continuous playback of a playlist with video files, and I was completely satisfied with it. But I’ve always wanted to learn how to create Lo fi streams 24/7: looped gif and music. It would seem that what could be simpler? And it’s easy if you have the means to rent a server with a graphical shell and install OBS there, or use ready-made solutions “out of the box” from Onbubble or
I heard about Liquidsoap and its extensive capabilities soon enough, but I was afraid of the need to understand the voluminous documentation and program the code myself (I have no programming experience at all). That’s why I crawled Github up and down in search of ready-made solutions, but everything I found either didn’t start from under my crooked hands, or worked extremely unstable. In general, I left this idea for almost a year, until the space on the disk was crammed to the brim with videos and the question arose about switching to a more expensive VPS configuration: the hoster did not provide for the build-up of individual components. I decided that this was a good reason to return to Liquidsoap, try to assemble a minimalistic script and change the radio format from showing videos to playing audio tracks. Anyway, the stream with videos confused many people, despite the fact that all the information was in the title of the stream and in the description, and the videos themselves were obviously edited. The most frequent phrase in the chat: “Is this a record?”.
Anyway, it wasn’t that difficult. It was just necessary to be patient and consistently read the first few chapters of The Liquidsoap book, and then separate parts of that manual, in order to assemble a working stream, and subsequently upgrade it with different features. So, if you suddenly get carried away with “radio engineering” or want to add something to the script that I will share, then this Liquidsoap book is your faithful assistant.

Limitations and What is important to understand?

  • I am neither a programmer nor a network administrator, but just an enthusiast. Therefore, it is possible that some points or formulations may seem silly and ridiculous to knowledgeable specialists.
  • The version of Liquidsoap in this guide is 2.1.2.
  • The VPS operating system in this guide is Ubuntu 20.04 (on 22.04, the installation at the opam install stage ends on a weak configuration).
  • The installation will contain only the necessary minimum of libraries for this setup.
  • It is desirable to install Liquidsoap on a clean system. Or stop ALL possible processes on the server (especially if the VPS is low-power, as in this case). The Ocaml compiler is very voracious on resources, so it is likely that it will give an error during the installation of the necessary components.
  • Alerts and widgets (for example, Streamlabs) cannot be easily added to the scene. Liquidsoap is not compatible with such web sources by default. There may be a way to do this, but I don’t understand exactly how yet. However, if you paid attention, then on 24/7 streams, not that widgets, even track names are not always displayed.
  • VPS configuration, which copes with 720p stream (CPU load from 70 to 90%) is 1 CPU 2400MHz and 1 GB RAM on Video codec preset: Veryfast. 1080p, unfortunately does not pull – even with Superfast preset, the load is under 100% and lags.
  • The disk space that the system and all installed components will take is about 4.5GB.
  • Make sure that your hoster provides a web shell with a file manager to the VPS file system. This will greatly simplify text editing and file uploading. Or master the connection to the VPS via FTP for these purposes.
  • Also, a web shell for the terminal/command line will be a plus. Despite the fact that I will explain the entire installation through the connection from Windows Powershell (this is more convenient and faster) – the web shell will simplify starting/stopping the script in the future.
  • As a rule, in paid tariffs, Internet traffic is unlimited. But it is also worth paying attention to this and calculating traffic in advance if its volume is limited by a monthly limit: the number of seconds per month x bitrate = our traffic.
    For example, we plan to stream in 720p resolution with a bitrate of 1500kbps (1.5 mbps):
    2592000 seconds per month X 1.5 mbps bitrate = 3888000mb, convert this value to Terabytes (usually traffic is specified in TB) through any online converter of values and get 0.48 TB per month. Therefore, 1 TB of traffic per month will be more than enough to broadcast in 720p.
  • There is a large selection of VPS on the market. I have no experience using different hosters, so I can’t advise anything. I myself, for convenience, chose the same provider –, which hosts my site and where there are web shells for the file manager and terminal, as well as unlimited traffic with a wide channel.

Installing Liquidsoap

The VPS is rented, its IP address and root user password are received – you can connect.
To do this, open the Windows Powershell utility and enter the command:

ssh root@your_vps_ip

The system will ask for a password and after entering it correctly (the entered characters or “*” will not be visible on the screen – this is ok), the connection will take place.

Important! The usual combinations for copying/pasting from the buffer do not work in the command line. CTRL+C - stops the currently running process! In Powershell, copy/paste is done with a "right click" of the mouse!
With the "Up" key, you can call the last entered commands - so as not to type them again every time.

The first step is to create a new user and set a password:

sudo adduser YOUR_NICKNAME

Now it is important to grant this user the necessary rights:

sudo usermod -aG sudo YOUR_NICKNAME

It remains to log in under this user:


In the future, you should connect to the server with this user, because all installed components will be assigned to this account (and Liquidsoap does not particularly like running as root):

 ssh YOUR_NICKNAME@your_vps_ip

First of all, you should install the library that you will need to display the track metadata:

sudo apt-get install libfreetype6-dev

To correctly install Liquidsoap and its dependencies, you need OPAM (Ocaml Package Manager) at least version 2.0:

sudo apt-get install opam


opam init

When the installation is completed, you need to enter a command to synchronize OCaml with the current environment (this should be done every time you see the message “run eval $(opam env)” in the log):

eval $(opam env)

And also OCaml version no lower than 4.12.0. I suggest installing 4.14.0, taking an example from the instructions

opam switch create cs3110-2022fa ocaml-base-compiler.4.14.0

It will take some time, be patient and don’t be scared if it seems that the system is stuck.
And again

eval $(opam env)

Now you can proceed directly to the installation of Liquidsoap itself. First you need to make a “request” that downloads all the missing components for the requested libraries:

opam depext gd ffmpeg liquidsoap

And then install:

opam install gd ffmpeg liquidsoap

Where: gd – is a plugin that will be needed to display text, ffmpeg – is an audio/video stream encoder, liquidsoap – is Liquidsoap with a set of standard libraries.

UPD: The 'opam depext/install liquidsoap' commands will install the latest version of Liquidsoap!
As of today (July 2023), a new version of Liquidsoap 2.2.0 has been released, in which a number of changes have been made and which may consume more CPU resources.
Therefore, I recommend installing version 2.1.4 (the latest in the 2.1.x branch and with a requirement for an Ocaml version of at least 4.13.0).
The full command in this case will look like this:
opam depext gd ffmpeg liquidsoap.2.1.4
opam install gd ffmpeg liquidsoap.2.1.4

And for the last time for today:

eval $(opam env)

Ready! To make sure that everything went smoothly, you can check the version of Liquid soap and the list of installed libraries:

liquidsoap --version
opam list


The first step is to create folders where the script, background animation and music will be located. It is desirable to do everything from the console on behalf of the created user in order to avoid conflicts of roles and rights in the system (LInux is quite scrupulous in this regard):

mkdir /home/YOUR_NICKNAME/radio
mkdir /home/YOUR_NICKNAME/radio/music

Now you can use the nano text editor to create a file (you can open it again with the same command):

nano /home/YOUR_NICKNAME/radio/stream.liq

Copy and paste the contents from this template:

#permission to run the script from the root user

#metadata functions
song_author = ref('')
def apply_song(m) =
song_author := m["artist"]

song_title = ref('')
def apply_song2(m) =
song_title := m["title"]

def get_track_name_text()
"$(artist) - $(title)" % [ 
("artist", !song_author),    
("title", !song_title)

#audio source
audio = playlist(reload_mode="watch", "/home/YOUR_NICKNAME/radio/music")
audio = mksafe(audio)

#video source
background = single("/home/YOUR_NICKNAME/radio/background.gif")

#calling metadata

background = video.add_text(color=0xFFFFFF, font="/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", speed=0, x=50, y=50, size=26,

#mixing sources
radio = mux_video(video=background, audio)

url = "rtmp://localhost/live"
enc = %ffmpeg(format="flv",
%video(codec="libx264", width=1280, height=720, pixel_format="yuv420p",
b="750k", maxrate="750k", minrate="750k", bufsize="1500k", profile="Main", preset="veryfast", framerate=30, g=60),
%audio(codec="aac", samplerate=44100, b="128k"))

output.url(fallible=true, url=url, enc, radio)
UPD: if you did install version 2.2.0, then the syntax has changed a little (instead of '!x', should be written 'x()') and the metadata block will look like this:
def get_track_name_text()
"$(artist) - $(title)" % [ 
("artist", song_author()),    
("title", song_title())

To save the changes to nano, press F3 and confirm the changes.
F2 to exit the editor.

Let’s figure it out and edit:

  • The # symbol before the line indicates that this line is “commented out” and does not affect the code.
  • settings.init.allow_root.set(true) – just in case (if suddenly you are too lazy to create a new user), it is allowed to run the script by the root user. Technically, you can delete it.
  • It is better not to touch the “Metadata Functions” block – these functions collect information about tracks from mp3 file tags.
  • audio source.
    * audio – variable name; playlist – playback mode of files from a folder (random is enabled by default); reload_mode = “watch” – means that the playlist will be updated as soon as a new file gets into the folder; the path to the folder is “/path/to/folder”. <- don’t forget to edit the directory!
    * audio = mksafe(audio) – making the source “safe”. That is, if something happens to the music folder, the script will not break, but will switch to silence playback.
    • video source
      * background – is the name of the variable; single – is the playback mode of a single file and the path to it. By default, it is looped. You can specify gif animation, jpg/png images, mp4 videos. <- don’t forget to edit the directory!
    • In the “Calling metadata” block, – the script accesses the functions from the “metadata functions” block to collect the “Artist – Song” for the currently playing track.
    • The “Text rendering” block. It specifies which text (get_track_name_text) and over which source (background) the text should be drawn. As well as the attributes of this text:
      *color=0xFFFFFF – text color, white is set in this example. HTML markup codes are suitable: the code must be placed after “0x”. For example, the yellow color from this site will look like this: color=0xFCB900.
      *font=”/path/to/font” – specifies the font to draw. This example uses the standard Linux path and font. For convenience, I recommend throwing fonts into the same folder where the script itself is located.
      *speed=0 – means that the text is static. If you change the value, the text will float across the screen.
      *x=50, y= 50 – the coordinates where the text will be located. x=0, y=0 is the upper left corner. It is important to note that Liquidsoap does not provide ready-made tools for text alignment (in any case, I could not figure out or come up with a mathematical formula for this). Since the template relies on Lofi radio stations, in this example the text is placed in the upper left corner with a small margin from the edges.
    • *size= – font size.
    • “Mixing”. Or more correctly, “Multiplexer”. Roughly speaking, we combine video and audio into a single source.
    • The “rtmp+codec” block. Here it is determined to which address to send the stream and the settings for the encoder are specified.
      * url = “rtmp://” – here the rtmp address and the stream key, which are issued by the streaming platform. If you want to stream to several sites at once or just improve the stability of the stream, I recommend additionally installing and configuring an Nginx server with an Rtmp module. Not so long ago I published a detailed guide on this topic.
      *enc = – encoder settings. This is where the field of creativity, experimentation and personal preferences begins. Codec (both for video and audio) and pixel_format, I recommend not to touch if you don’t know how to use it.
      *width=, height= – the desired resolution in pixels (1920 x 1080 / 1280 x 720 and so on).
      *b= – bitrate. To set a constant bitrate (CBR), which is often recommended by stream services, it is necessary to duplicate the value of “b” in maxrate and minrate. Buffer size “bufsize” – usually set just twice the bitrate.
      *Then the standard values profile=”Main” and preset =”veryfast”.
      *framerate=, g= – the number of frames per second and the number of keyframes. I can be very wrong here. But, as far as I understand, then g (GOP-size) – you need to specify twice as much framerate (frames per second) and then it turns out that keyframes will be inserted every 2 seconds.
      The selection of bitrate is obviously a big topic for discussion. For myself, I decided to rely on the bitrate and framerate of the source video for the background by looking at them in the file properties and setting close values in the encoder. In any case, the lower the bitrate, the better the broadcastability, which is a fundamental factor for radio.
      *For the audio codec, we set the sampling rate parameters – samplerate = 44100 (classic) and bitrate b = – here to taste. But obviously not more than 320k. And so, as far as I can tell, radio stations rarely set the bitrate above 128k.
    • The last line of the code takes information from the encoder, takes a mixed source and sends it to the specified rtmp-address.

This was sorted out. It remains to upload the tracks to the music folder via a file manager or FTP and start launching.


To run the script, you need to go to the folder where it is stored:

cd /home/YOUR_NICKNAME/radio

И ввести команду:

liquidsoap NAME_OF_SCRIPT.liq

Or if you completely follow this guide, then:

liquidsoap stream.liq

Lines will run across the screen and if something is wrong, then it will immediately stop working: you need to go into the script and look, maybe a mistake has been made somewhere (the parenthesis is not closed, the comma is omitted, and so on).
If everything is fine, then the stream will start and you can see firsthand the result of your efforts.

But if you close Powershell, the script will stop working and the stream will drop.

If the hoster has a web shell for the terminal, then you need to open it and log in on behalf of the created user. And repeat the startup procedure: go to the folder with the script and run it. The browser can be closed – the script will continue to work.
If you need to change something in the script, then after making edits, you need to open this web terminal (you will see that the process is running), stop the process (CTRL+C), press the “Up” key to call the last command (it was the script launch) and activate Liquidsoap again.

If there is no terminal web shell, or you just want to manage everything from Powershell, then you should run the command with the following attributes to activate work in the background:

nohup liquidsoap stream.liq &

After that, you can safely close the Powershell window. When you reconnect to the server, you will not see running lines in the log. Therefore, to stop the script, you need to “kill” the background process:

killall liquidsoap

UPD: There is an addition to this guide:, with examples of how you can improve your radio stream.
Inline Feedbacks
View all comments