This replaces and combines two earlier posts:
- Replacing syslogd with syslog-ng (sysutils/syslog-ng) on FreeBSD.
- Getting Nginx logs into Victoria-Logs
I just got Victoria Logs running. Now I want to get logs into it. For starters, I want to get the logs on the host itself into Victoria Logs. I figure this is a log-risk experiment.
In this post:
- FreeBSD 15.0
- victoria-logs-1.50.0_2
- syslog-ng-4.11.0_2
- I’m installing syslog-ng on a host (x8dtu)
- The nginx logs reside in a jail (nginx01) on that host
I will be using TLS between syslog-ng and victoria-logs – you will notice this is all on the same host, and some of you might say: you don’t need TLS for that. However, this is a proof-of-concept for remote hosts. They will be passing traffic through my VPN and my network. Or perhaps over the Internet. Things change. Let’s start with TLS.
I know that various tools can be used for testing logs to VictoriaLogs. I should investigate that. Filebeat (now known as beats) has been mentioned.
The install
I installed:
[17:03 x8dtu dvl ~] % sudo pkg install syslog-ng
Updating local repository catalogue...
Fetching meta.conf: 100% 179 B 0.2 kB/s 00:01
Fetching data: 100% 353 KiB 361.3 kB/s 00:01
Processing entries: 100%
local repository update completed. 955 packages processed.
All repositories are up to date.
The following 6 package(s) will be affected (of 0 checked):
New packages to be INSTALLED:
glib: 2.86.4,2 [local]
ivykis: 0.43.2_1 [local]
json-c: 0.18 [local]
libuuid: 2.42.1 [local]
py312-packaging: 26.2 [local]
syslog-ng: 4.11.0_2 [local]
Number of packages to be installed: 6
The process will require 112 MiB more space.
12 MiB to be downloaded.
Proceed with this action? [y/N]: y
[1/6] Fetching ivykis-0.43.2_1: 100% 70 KiB 71.3 kB/s 00:01
[2/6] Fetching py312-packaging-26.2: 100% 189 KiB 193.9 kB/s 00:01
[3/6] Fetching glib-2.86.4,2: 100% 10 MiB 5.5 MB/s 00:02
[4/6] Fetching syslog-ng-4.11.0_2: 100% 1102 KiB 1.1 MB/s 00:01
[5/6] Fetching libuuid-2.42.1: 100% 48 KiB 49.5 kB/s 00:01
[6/6] Fetching json-c-0.18: 100% 71 KiB 73.1 kB/s 00:01
Checking integrity... done (0 conflicting)
[1/6] Installing ivykis-0.43.2_1...
[1/6] Extracting ivykis-0.43.2_1: 100%
[2/6] Installing json-c-0.18...
[2/6] Extracting json-c-0.18: 100%
[3/6] Installing libuuid-2.42.1...
[3/6] Extracting libuuid-2.42.1: 100%
[4/6] Installing py312-packaging-26.2...
[4/6] Extracting py312-packaging-26.2: 100%
[5/6] Installing glib-2.86.4,2...
[5/6] Extracting glib-2.86.4,2: 100%
[6/6] Installing syslog-ng-4.11.0_2...
[6/6] Extracting syslog-ng-4.11.0_2: 100%
==> Running trigger: glib-schemas.ucl
Compiling glib schemas
No schema files found: doing nothing.
==> Running trigger: gio-modules.ucl
Generating GIO modules cache
=====
Message from syslog-ng-4.11.0_2:
--
syslog-ng is now installed! To replace FreeBSD's standard syslogd
(/usr/sbin/syslogd), complete these steps:
1. Create a configuration file named /usr/local/etc/syslog-ng.conf
(a sample named syslog-ng.conf.sample has been included in
/usr/local/etc). Note that this is a change in 2.0.2
version, previous ones put the config file in
/usr/local/etc/syslog-ng/syslog-ng.conf, so if this is an update
move that file in the right place
2. Configure syslog-ng to start automatically by adding the following
to /etc/rc.conf:
syslog_ng_enable="YES"
3. Prevent the standard FreeBSD syslogd from starting automatically by
adding a line to the end of your /etc/rc.conf file that reads:
syslogd_enable="NO"
4. Shut down the standard FreeBSD syslogd:
kill `cat /var/run/syslog.pid`
5. Start syslog-ng:
/usr/local/etc/rc.d/syslog-ng start
Configuration tweaks & permissions issues
I added this section after publishing the post. There are some permission issues and local logging issues for me to fix. This is how I did that.
By default, syslog-ng does a chmod 0700 when it opens the file. I wanted to preserve existing permissions, mostly for logcheck usage, but also for my sanity. Here are those changes:
[17:18 x8dtu dvl ~] % diff -ruN /usr/local/etc/syslog-ng.conf.sample /usr/local/etc/syslog-ng.conf
--- /usr/local/etc/syslog-ng.conf.sample 2026-06-18 00:42:01.000000000 +0000
+++ /usr/local/etc/syslog-ng.conf 2026-06-20 17:18:32.813616000 +0000
@@ -20,10 +20,10 @@
#
# destinations
#
-destination messages { file("/var/log/messages"); };
+destination messages { file("/var/log/messages" perm(0644)); };
destination security { file("/var/log/security"); };
-destination authlog { file("/var/log/auth.log"); };
-destination maillog { file("/var/log/maillog"); };
+destination authlog { file("/var/log/auth.log" perm(0640)); };
+destination maillog { file("/var/log/maillog" perm(0640)); };
destination lpd-errs { file("/var/log/lpd-errs"); };
destination xferlog { file("/var/log/xferlog"); };
destination cron { file("/var/log/cron"); };
[17:18 x8dtu dvl ~] %
Getting ready
Here, I disable the built-in syslogd and enable the new syslog-ng.
[17:19 x8dtu dvl ~] % sudo service syslogd disable syslogd disabled in /etc/rc.conf [17:19 x8dtu dvl ~] % sudo service syslog-ng enable syslog_ng enabled in /etc/rc.conf [17:19 x8dtu dvl ~] %
I’m told the default configuration file works as a drop-in replacement for the system defaults.
Stop, and start:
[17:16 x8dtu dvl ~] % sudo service syslogd onestop Stopping syslogd. Waiting for PIDS: 3138. [17:18 x8dtu dvl ~] % sudo service syslog-ng start Starting syslog_ng. [17:19 x8dtu dvl ~] % ps auwwxd | grep syslog root 845 0.0 0.0 14368 2888 - Is 10Jun26 0:01.46 |-- rtsold: system.syslog (rtsold) dvl 21692 0.0 0.0 14164 2680 0 S+ 17:19 0:00.00 | `-- grep syslog root 4517 0.0 0.0 14428 3216 - ICsJ 10Jun26 0:06.39 |-- /usr/sbin/syslogd -s root 4520 0.0 0.0 14428 3076 - IJ 10Jun26 0:00.01 |-- syslogd: syslogd.casper (syslogd) root 4521 0.0 0.0 14428 3036 - IsJ 10Jun26 0:00.00 |-- syslogd: system.net (syslogd) root 5945 0.0 0.0 14428 3212 - ICsJ 10Jun26 2:24.58 |-- /usr/sbin/syslogd -s root 5971 0.0 0.0 14428 3068 - IJ 10Jun26 0:00.01 |-- syslogd: syslogd.casper (syslogd) root 5974 0.0 0.0 14428 3032 - IsJ 10Jun26 0:00.00 |-- syslogd: system.net (syslogd) root 7993 0.0 0.0 14428 3208 - SCsJ 10Jun26 0:01.30 |-- /usr/sbin/syslogd -s root 7996 0.0 0.0 14428 3080 - IJ 10Jun26 0:00.01 |-- syslogd: syslogd.casper (syslogd) root 7997 0.0 0.0 14428 3036 - IsJ 10Jun26 0:00.00 |-- syslogd: system.net (syslogd) root 8549 0.0 0.0 14428 3296 - ICsJ 10Jun26 0:16.94 |-- /usr/sbin/syslogd -s root 8552 0.0 0.0 14428 3132 - IJ 10Jun26 0:00.02 |-- syslogd: syslogd.casper (syslogd) root 8553 0.0 0.0 14428 3096 - IsJ 10Jun26 0:00.00 |-- syslogd: system.net (syslogd) root 10829 0.0 0.0 14428 3280 - SCsJ 10Jun26 0:28.53 |-- /usr/sbin/syslogd -s root 10832 0.0 0.0 14428 3148 - IJ 10Jun26 0:00.01 |-- syslogd: syslogd.casper (syslogd) root 10833 0.0 0.0 14428 3104 - IsJ 10Jun26 0:00.00 |-- syslogd: system.net (syslogd) root 21680 0.0 0.0 27040 12368 - S 17:19 0:00.00 |-- /usr/local/sbin/syslog-ng -f /usr/local/etc/syslog-ng.conf -p /var/run/syslog.pid root 21681 0.0 0.0 40776 15476 - Ss 17:19 0:00.04 | `-- /usr/local/sbin/syslog-ng -f /usr/local/etc/syslog-ng.conf -p /var/run/syslog.pid
Well, that was painless. However, let’s check logs.
[17:19 x8dtu dvl ~] % logger testing [17:20 x8dtu dvl ~] % sudo tail /var/log/messages Jun 20 16:50:46 x8dtu openvpn[3319]: Control Channel: TLSv1.3, cipher TLSv1.3 TLS_AES_256_GCM_SHA384, peer certificate: 4096 bits RSA, signature: RSA-SHA256, peer signing digest/type: rsa_pss_rsae_sha256 RSASSA-PSS, key agreement: X25519MLKEM768 Jun 20 17:08:34 x8dtu pkg[12806]: ivykis-0.43.2_1 installed Jun 20 17:08:34 x8dtu pkg[12806]: json-c-0.18 installed Jun 20 17:08:34 x8dtu pkg[12806]: libuuid-2.42.1 installed Jun 20 17:08:34 x8dtu pkg[12806]: py312-packaging-26.2 installed Jun 20 17:08:36 x8dtu pkg[12806]: glib-2.86.4,2 installed Jun 20 17:08:36 x8dtu pkg[12806]: syslog-ng-4.11.0_2 installed Jun 20 17:17:54 x8dtu syslogd: exiting on signal 15 Jun 20 17:19:22 x8dtu syslog-ng[21681]: syslog-ng starting up; version='4.11.0' Jun 20 17:20:00 x8dtu dvl[21757]: testing
That seems to be working.
Next step get logs into victoria-logs.
syslog messages
I’m using include files with my configuration. This is the first of such files. This sends the syslog messages to VictoriaLogs.
[17:32 x8dtu dvl /usr/local/etc] % cat syslog-ng-local-syslog.conf
template t_victoria_syslog_json {
template("$(format-json
_time=$ISODATE
_msg=$MESSAGE
host=$HOST
program=$PROGRAM
)\n");
};
destination d_victorialogs_syslog_json {
http(
persist-name("d_victorialogs_syslog_json")
url("https://logs.int.unixathome.org:9428/insert/jsonline?_stream_fields=host,program")
method("POST")
headers("Content-Type: application/json")
body(t_victoria_syslog_json)
tls(peer-verify(yes))
disk-buffer(
disk-buf-size(1073741824)
reliable(yes)
)
workers(2)
);
};
log {
source(src);
destination(d_victorialogs_syslog_json);
};
I added the following line to the bottom of /usr/local/etc/syslog-ng.conf:
@include "/usr/local/etc/syslog-ng-local-syslog.conf"
After restarting syslog-ng on x8dtu, I started to see these messages in VictoriaLogs:
{
"_msg": "syslog-ng starting up; version='4.11.0'",
"_stream": "{host=\"x8dtu\",program=\"syslog-ng\"}",
"_stream_id": "0000000000000000f5ee0a1fa8262b11ba75d81442c00a6f",
"_time": "2026-06-20T17:37:15Z",
"host": "x8dtu",
"program": "syslog-ng"
}
Success. Next, nginx logs.
Telling nginx to do JSON
This is the log format I use to tell nginx how to produce JSON:
log_format victorialogs_json escape=json '{'
'"_time":"$time_iso8601",'
'"_msg":"$request",'
'"status":"$status",'
'"remote_addr":"$remote_addr",'
'"body_bytes_sent":"$body_bytes_sent",'
'"request_time":"$request_time",'
'"http_user_agent":"$http_user_agent",'
'"app":"nginx",'
'"http_referer":"$http_referer",'
'"request_method":"$request_method",'
'"host":"$host",'
'"hostname":"$hostname",'
'"server_name":"$server_name"'
'}';
Note the hardcoded app field.
The log_format statement goes outside the server declaration but inside the http directive.
The log directive, as shown below, goes inside the server declaration. I keep the existing original logs (lines 1-2). The new lines are lines 4-5.
error_log /var/log/nginx/freshports.org-error.log; access_log /var/log/nginx/freshports.org-access.log combined; # Send JSON format to a log agent or Syslog endpoint handled by VictoriaLogs access_log /var/log/nginx/access_json.log victorialogs_json;
Issuing a reload (or restart) will start logging to the new file. That has to be done inside the jail, in my case. I think I could do it from outside, but not today.
Checking the log file, I see this (newlines added to fit on a page):
[17:58 x8dtu-nginx01 dvl /usr/local/etc/freshports] % tail -1 /var/log/nginx/access_json.log
{"_time":"2026-06-20T17:59:14+00:00","_msg":"GET /foo/?branch=2026Q2 HTTP/1.1","status":"403","remote_addr":"71.168.156.244","body_bytes_sent":"183","request_time":"0.000",
"http_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 14_3_1) AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/131.0.6778.204 Safari/537.36","app":"nginx","http_referer":"","request_method":"GET",
"host":"x8dtu.example.org","hostname":"x8dtu-nginx01.vpn.unixathome.org","server_name":"x8dtu.example.org"}
Good.
Next, we tell syslog-ng to pick up and transmit that file to the server.
Sending nginx logs to the server
The following instructs syslog-ng to send the nginx logs to the server.
[17:30 x8dtu dvl ~] % cat /usr/local/etc/syslog-ng-nginx-to-victoria-logs.conf
source s_nginx_json {
file("/jails/nginx01/var/log/nginx/access_json.log" flags(no-parse) log-msg-size(65536));
};
destination d_victorialogs_nginx_json {
http(
persist-name("d_victorialogs_nginx_json")
url("https://logs.int.unixathome.org:9428/insert/jsonline?_stream_fields=host,program")
method("POST")
headers("Content-Type: application/json")
body("${MESSAGE}\n")
tls(peer-verify(yes))
);
};
log {
source(s_nginx_json);
destination(d_victorialogs_nginx_json);
};
Notes:
- _stream_fields tells VictoriaLogs to use host and program for the _stream value in the stored record.
I added the following line to the bottom of /usr/local/etc/syslog-ng.conf:
@include "syslog-ng-nginx-to-victoria-logs.conf"
Let’s pick up the changes:
[17:57 x8dtu dvl /usr/local/etc] % sudo service syslog-ng restart Stopping syslog_ng. Waiting for PIDS: 41691. Starting syslog_ng.
And boom, this entry was located in VictoriaLogs:
{
"_msg": "GET /foo/?branch=2026Q2 HTTP/1.1",
"_stream": "{host=\"x8dtu.example.org\"}",
"_stream_id": "0000000000000000ad432b7122308cfa678384b77ffc7f89",
"_time": "2026-06-20T17:59:14Z",
"app": "nginx",
"body_bytes_sent": "183",
"host": "x8dtu.example.org",
"hostname": "x8dtu-nginx01.vpn.unixathome.org",
"http_user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 14_3_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.204 Safari/537.36",
"remote_addr": "71.168.156.244",
"request_method": "GET",
"request_time": "0.000",
"server_name": "x8dtu.example.org",
"status": "403"
}
Success!
Notice how _stream is only the host. Later logs contained both app and host: _stream: “{app=\”nginx\”,host=\”www.freshports.org\”}”
Hope that helps.











