BysMax

Setting up media.path in Traccar: Saving and Serving Camera Photos

14 min

If you already have a device with a compatible camera, enabling photo storage in Traccar is surprisingly simple: a single configuration key. What isn't so simple is everything that comes after — disk, permissions, and the proxy. Let's go through it piece by piece.

The real traccar.xml keys

There are only two keys related to multimedia. These:

<entry key='media.path'>/opt/traccar/media</entry>
<entry key='media.bufferSize'>33554432</entry>

media.path

This is the folder where the server stores audio, video, and photos. Its default value is ./media, relative to the server's working directory. Traccar automatically creates a subfolder per device using its unique identifier.

This key is also the switch. If media.path has a value, the servlet that serves the files gets registered; if not, it doesn't. Don't go looking for a media.enable key because it doesn't exist — it's a frequent mistake on the forum. Since the default value is already ./media, in practice the system is operational right out of the install; what's usually missing is pointing it to an absolute path with real space available.

media.bufferSize

The maximum size, in bytes, of a multimedia file a decoder can accumulate from a device. The default is 32 MB (33554432). Transfers that exceed that limit get discarded.

Two things to keep in mind:

  1. Only one buffer per connection is kept. Don't expect parallel transfers over the same socket.
  2. Cranking this value way up isn't free: it's memory the server reserves per active connection mid-transfer. If you're going to receive video clips from several vehicles at once, size your RAM accordingly.

For typical ADAS/DSM camera photos (100–500 KB) the default value is more than enough.

Where the files end up

The on-disk structure looks like this:

/opt/traccar/media/
├── 356938035643809/
│   ├── 1754400000000.jpg
│   └── 1754400600000.jpg
└── 862205059000000/
    └── 1754401200000.h265

One folder per uniqueId (the IMEI or identifier you registered), and inside it files named by timestamp. On the corresponding position, Traccar adds an image, video, or audio attribute whose value is just the file name, not the path.

How they're accessed from outside

Traccar exposes the folder at:

GET /api/media/{uniqueId}/{fileName}

For example:

curl -u user:password \
  https://your-server.com/api/media/356938035643809/1754400000000.jpg \
  -o photo.jpg

This is not a public folder. In front of it there's a filter that requires a valid session and checks that the authenticated user has permission over that specific device. A user on your platform can't pull another customer's photos by guessing IMEIs.

Disk space: the part that bites

Nobody plans for this and then the server runs out of disk three weeks later. Do the math beforehand:

  • A 300 KB event photo
  • Times 10 events a day
  • Times 50 vehicles
  • Times 30 days

4.5 GB a month. With video the figure easily balloons into tens of GB.

Two practical consequences:

  1. Mount media.path on a separate volume from the operating system. If it fills up, it shouldn't take down the database or the logs with it.

  2. Traccar's automatic cleanup of old positions does not delete the media files. If you purge the history, the JPGs are left orphaned on disk. You need your own cleanup job, for example:

    # Delete media files older than 90 days
    find /opt/traccar/media -type f -mtime +90 -delete
    

    Test it first without -delete and review the output before scheduling it in cron.

Permissions

The Traccar service needs write access to that folder. If you create it by hand as root, the service will fail silently on writes:

sudo mkdir -p /opt/traccar/media
sudo chown -R traccar:traccar /opt/traccar/media

Adjust the user to whatever your installation uses (in Docker it's usually different; there, what matters is mounting a persistent volume at the path you declare in media.path, or you'll lose your photos on every container recreation).

A note on the reverse proxy

If you have Nginx or Apache in front for HTTPS (and you should — see secure connection for Traccar), keep in mind that /api/media/ serves binary files that can be considerably heavier than a normal JSON API response.

If you see cut-off downloads or errors opening large video clips, check your proxy's response size limits and timeouts. In Nginx, the usual parameters to look at are proxy_read_timeout and response buffering.

And if you're also using JT1078 live video, there's a second path the proxy has to let through: /api/stream/, which serves the live.m3u8 playlist and the .ts segments. This is a classic failure: the video "won't start" when in fact the playlist loads fine but the segments 404 or get stuck in the proxy's buffering. I cover it in detail in JT1078 live video on Traccar.

NOTE

Traccar doesn't publish official proxy recommendations specific to media, so the above is general operating advice, not a configuration prescribed by the project. Start with the standard configuration and adjust only if you run into real failures.

If you have a JT808 MDVR, this doesn't apply to you

A heads-up to save you hours of debugging: if your unit is an MDVR that speaks JT808/JT1078, you can configure media.path perfectly well and still not see a single video file there. That protocol doesn't use the media system: Traccar acts as a live relay and the recorded history stays on the vehicle's SD card.

What's normal on those servers is finding the folder with nothing but a device.jpg (the device's profile picture, which is a separate mechanism entirely). It isn't broken: it's the design. I explain it in live JT1078 video in Traccar. If you're choosing hardware, it's worth checking which cameras actually work with Traccar before you buy.

Verifying it works

  1. Confirm your device uses a protocol with real media support — the list is short and I go through it in how the media system works.

  2. Trigger a photo (via command, or by causing the event that generates one).

  3. Check the folder:

    ls -la /opt/traccar/media/YOUR_IMEI/
    
  4. If the file is on disk but you don't see it in the interface, the problem is user permissions on the device or the proxy — not the decoder.

  5. If there's no file, check the server log: it's almost always that the protocol doesn't implement media, or that the transfer exceeded media.bufferSize.

Comentarios (0)