Hardware Inventory and Visualization in the Server Room: Deploying Rackula on Docker
1. Diagnosis: why your rack diagram in Excel always lies
You rack a new server. Slot it into the third unit from the top. Forget to update the spreadsheet. Six months later, someone other than the person who maintained that spreadsheet looks at it, and sees something completely different from what’s actually in the rack.
Sound familiar? Hardware inventory in a server room usually lives in one of three states: a paper label on the cabinet door, an Excel file on a shared drive that nobody has opened in a year, or a Visio diagram drawn once before an audit and forgotten right after.
All three break for the same reason: updating the diagram isn’t built into the process of installing hardware. You rack a server, then go configure the OS, not open a diagram editor.
Rackula solves this differently. You don’t draw the diagram ahead of time and reconcile it manually later. You drag the device block onto the right unit with your mouse, right while you’re planning the install, and the diagram immediately becomes live documentation instead of an archival artifact.
By the end of this article you’ll have:
- Rackula deployed in Docker with persistent data storage (persist mode)
- An understanding of the difference between client-side and server-side storage modes
- A working rack diagram with IP addresses and notes for each device
- A reverse proxy configured for access from your local network
- A list of five typical failures and commands for diagnosing them
- A backup scheme for your rack data
- An understanding of when Rackula isn’t the right fit, and what to use instead
This takes about twenty minutes of active time, not counting log reading on your first permissions mess. And there will be one, more on that below in the complications section.
2. Root causes: why manual rack inventory doesn’t work
Let’s break down exactly what breaks rack hardware tracking when you do it manually.
| Cause | Why it breaks the process |
|---|---|
| The diagram is separate from the install process | Updating documentation is a separate action after installation, not part of it. Separate actions get skipped first |
| The file lives on a file share, not a system with version history | No change history, no understanding of who last touched the diagram or when |
| The format doesn’t support device metadata | A rectangle in Visio doesn’t store an IP, serial number, or install date, just a label |
| No single source of truth for a distributed team | Every engineer has their own version of the file, synced manually over email |
| The barrier to entry for heavy CMDB systems is too high | NetBox and similar tools solve the problem, but deploying a full DCIM for a single rack is overkill |
| No visual tie to U-position | A text list of “what’s where” doesn’t answer “how much space is left” at a glance |
That last point is the most common. You open the spreadsheet, see a list of forty text rows, and still go physically count free units in the rack by hand. The whole point of documentation is lost.
3. The fix: deploying Rackula with persistent data
Prerequisites
You need Docker Engine and Docker Compose v2 on the host. Check versions before you start:
docker --version
docker compose version
Rackula ships in two image modes. The latest tag is a pure frontend, everything is stored in the browser via localStorage, data doesn’t survive a change of computer. The persist tag is the frontend plus a separate rackula-api container that writes YAML files to the host disk. For hardware inventory that the whole team looks at, only persist mode works. Client mode is for sketching a quick diagram and nothing more.
mkdir -p /opt/docker/rackula/data
cd /opt/docker/rackula
chown -R 1001:1001 data
Step 1. Docker Compose file
Create docker-compose.yml. Below is a working setup with resource limits and basic hardening, no reverse proxy yet, just for testing on a local port.
services:
rackula:
image: ghcr.io/rackulalives/rackula:persist
container_name: rackula
ports:
- "8080:8080"
environment:
- API_HOST=rackula-api
- API_PORT=${RACKULA_API_PORT:-3001}
- RACKULA_LISTEN_PORT=8080
- API_WRITE_TOKEN=${RACKULA_API_WRITE_TOKEN:-}
- RACKULA_AUTH_MODE=none
restart: unless-stopped
stop_grace_period: 10s
depends_on:
rackula-api:
condition: service_healthy
networks:
- rackula-network
deploy:
resources:
limits:
cpus: "0.50"
memory: 128M
reservations:
cpus: "0.10"
memory: 16M
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
read_only: true
tmpfs:
- /var/cache/nginx:size=10M
- /var/run:size=1M
- /tmp:size=5M
- /etc/nginx/conf.d:size=1M,uid=101,gid=101
rackula-api:
image: ghcr.io/rackulalives/rackula-api:latest
container_name: rackula-api
restart: unless-stopped
stop_grace_period: 10s
volumes:
- ./data:/data
networks:
- rackula-network
environment:
- DATA_DIR=/data
- RACKULA_API_PORT=${RACKULA_API_PORT:-3001}
- CORS_ORIGIN=${CORS_ORIGIN:-http://localhost:8080}
- RACKULA_API_WRITE_TOKEN=${RACKULA_API_WRITE_TOKEN:-}
- RACKULA_AUTH_MODE=none
- RACKULA_AUTH_SESSION_COOKIE_SECURE=false
- RACKULA_AUTH_CSRF_PROTECTION=true
- ALLOW_INSECURE_CORS=false
deploy:
resources:
limits:
cpus: "0.25"
memory: 128M
reservations:
cpus: "0.05"
memory: 32M
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
read_only: true
tmpfs:
- /tmp:size=5M
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:${RACKULA_API_PORT:-3001}/health"]
interval: 30s
timeout: 10s
start_period: 5s
retries: 3
networks:
rackula-network:
name: rackula-network
driver: bridge
Pay attention to the memory limit on rackula-api. The original examples in the docs set 64M, in practice that’s not enough, the container dies with OOM exit code 137 on the very first device library import. Set 128M right away, it’ll save you an evening of log diving.
Step 2. Environment variables file
RACKULA_API_WRITE_TOKEN=generate_a_random_32_char_string
CORS_ORIGIN=http://YOUR_HOST_IP:8080
CORS_ORIGIN must match, character for character, the address you use to open the interface in your browser. If the protocol or port doesn’t match, you’ll get a silent write failure with no clear error on screen.
Step 3. Startup
docker compose up -d
docker compose logs -f
Result: both containers in running status, rackula-api healthy. The rackula-api logs shouldn’t contain any permission denied lines for writes to /data.
Step 4. Reverse proxy
Don’t expose port 8080 directly to the internet, Rackula has no built-in brute-force protection unless you set up OIDC. It makes more sense to put the service behind your usual reverse proxy with basic auth or behind an SSO portal. Example for nginx:
server {
listen 443 ssl http2;
server_name rackula.example.local;
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
After publishing over HTTPS, don’t forget to change CORS_ORIGIN to the new address and enable RACKULA_AUTH_SESSION_COOKIE_SECURE.
Deployment architecture
4. Verification: is the inventory working correctly
After starting the stack, check three things in order: container status, API health, and status in the interface itself.
docker compose ps
Both services should be Up, rackula-api should be Up (healthy). Next, check the health endpoint directly:
curl -s http://127.0.0.1:3001/health
Open the web interface in a browser. The top right corner should say Online, not Offline. Create a test rack, drag one device block, refresh the page. If the block stays in place after the refresh, saving in persist mode is working.
The last test: save your data, stop the stack completely, and bring it back up:
docker compose down
docker compose up -d
The rack should appear right where it was as soon as the page loads, no manual file import needed. That’s the difference between persist mode and regular client-side mode.
5. Complications: what breaks most often
Usually this is where people write “don’t worry, it’s not that scary.” I won’t lie: almost everyone trips over permissions on the first run. The good news is it’s fixed with a single command.
| Error | Cause | Fix |
|---|---|---|
| “Server offline or unavailable” in the interface | The data directory is owned by root, the container can’t write to it | Run chown -R 1001:1001 data and restart rackula-api |
| rackula-api exits with code 137, status unhealthy | OOM Kill, the 64M memory limit from the typical doc example is too small | Raise the memory limit to 128M or more in deploy.resources.limits |
| Blank screen or network error on save | CORS_ORIGIN in .env doesn’t match the actual address used to access the interface | Set CORS_ORIGIN to match the browser address bar exactly, restart rackula-api |
| A different computer doesn’t show the same racks | You deployed the client-side latest tag instead of persist, or both computers point at different API instances | Make sure the frontend image is specifically persist and both clients hit the same rackula-api address |
| Can’t select a device on a carrier module (half-width) | Known issue with selecting nested devices in older builds | Update to the current release, the carrier device model was reworked in the 26.x release series |
| After an update, OIDC login loops with ERR_TOO_MANY_REDIRECTS | Since the release that changed the default redirect URI, the old callback path is no longer served | Register the /auth/callback path with your identity provider, or pin the old path via RACKULA_OIDC_REDIRECT_URI |
| docker compose up fails on rackula-api dependency being unhealthy | The healthcheck doesn’t pass within the 5 second start_period on a slower host | Increase the health start_period to 15-20 seconds, rebuild and restart |
6. Alternatives: when Rackula isn’t the right fit
Rackula isn’t the only option for server room hardware inventory, and not always the best one. Here’s what you should actually compare it against.
| Tool | When to pick it instead of Rackula |
|---|---|
| NetBox | You need a full CMDB as a source of truth for Ansible, IPAM, cable tracking, and virtualization, not just a visual diagram |
| RackTables | Your team already runs legacy infrastructure on a PHP stack and doesn’t want to add a new Docker service |
| GLPI | You need inventory alongside a service desk and license tracking, not just physical placement |
| count.racku.la (public hosting) | You need a one-off diagram without deploying your own infrastructure, data is stored in the browser only |
| Excel / Visio | Honestly, almost never pick this if you’ve read this far |
The case for Rackula in this article is simple: the barrier to entry is lower than NetBox’s, and the output is visual, not just tabular. If you need a full CMDB with Ansible inventory integration, go straight to NetBox, don’t waste time on a halfway solution.
7. Prevention: don’t lose your rack data
Rackula’s data in persist mode is just YAML files in the data directory. That means backing it up is as simple as backing up any config.
| What | How often | Where |
|---|---|---|
| The entire ./data directory | Daily, via cron | A separate backup server via rsync or BorgBackup |
| The docker-compose.yml and .env files | On every change | Git infrastructure repo, no secrets in plain text |
| Container images | Before every update | A local registry or the latest tag minus one, for quick rollback |
borg create --stats /path/to/repo::rackula-{now} /opt/docker/rackula/data
One lost data folder without a backup isn’t a disaster, it’s just an evening spent rebuilding the diagram from memory and old screenshots in your Telegram chat.
For auto-start after a host reboot, the unless-stopped restart policy in docker-compose.yml already covers that, nothing extra needed. For availability monitoring, add an HTTP status check on port 8080 to your monitoring system, whether that’s Zabbix or something simpler.
8. FAQ: common questions about rack hardware inventory
Why doesn’t saving a rack work after setting up Rackula?
Most often it’s permissions on the data directory, the rackula-api container writes as UID 1001, but the directory was created by root. Change the owner with chown and restart the container.
How do I check that Rackula is working correctly?
Three checks in a row: docker compose ps container status, the /health endpoint on the API port, and the Online status in the top right corner of the interface after loading the page.
What do I do if rackula-api keeps crashing?
Check the container’s exit code. Code 137 means OOM Kill, the container doesn’t have enough memory for the limit set in docker-compose.yml. Raise the memory limit to 128M or higher.
How is Rackula different from NetBox for server room inventory?
Rackula is about a visual rack diagram and physical layout, deployable in 15 minutes. NetBox is a full CMDB with IPAM, cable tracking, and Ansible integration, but it takes considerably more time to set up and requires understanding its data schema.
9. What to expect
After this setup you have Rackula running in persist mode, data written to YAML on the host disk instead of living only in one engineer’s browser. The rack diagram updates right at the moment hardware gets installed, instead of turning into a separate “someday” task.
From here it’s a matter of habit. Make it a rule: rack a unit, immediately drag the block into Rackula, before you go configure the OS. In a month, the rack diagram will be more accurate than any spreadsheet the whole team maintained for six months. If after setup the status still shows offline or the container keeps crashing for reasons other than the ones described above, leave a comment and we’ll sort it out.
System requirements
| Component | Minimum version |
|---|---|
| Docker Engine | 24.x or newer |
| Docker Compose | v2 (compose plugin, not standalone docker-compose 1.x) |
| Frontend image | ghcr.io/rackulalives/rackula:persist |
| API image | ghcr.io/rackulalives/rackula-api:latest |
| Host RAM | at least 512 MB free beyond container limits |
At the time of publication, the image tags above are current. Before installing, check the latest releases in the project’s GitHub Releases, versions come out frequently, and the changelog sometimes requires manual action on update (for example, the OIDC redirect URI change in one release).
Port table
| Port | Protocol | Purpose | Exposed externally? |
|---|---|---|---|
| 8080 | HTTP/TCP | Rackula web interface (frontend) | Through a reverse proxy, not directly |
| 3001 | HTTP/TCP | Rackula API, reading and writing YAML | No, internal to the docker network only |
Security
- Never expose port 3001 externally under any circumstances, it’s direct access to write operations
- Block port 8080 on the host firewall, publish only through a reverse proxy with TLS
- Use RACKULA_API_WRITE_TOKEN, without a token, writes are open to anyone who can reach the API
- If your rack data is sensitive, put basic auth on the reverse proxy or set up OIDC through an SSO provider
- Keep RACKULA_AUTH_SESSION_COOKIE_SECURE=true when running over HTTPS, otherwise the session cookie goes out in plain text
- The containers already run with read_only and cap_drop ALL in the example compose file, don’t remove these options without good reason
Updating
Always read the release changelog before updating, Rackula moves fast, and some changes require editing .env, as happened with the OIDC redirect URI.
docker compose pull
docker compose up -d
docker compose logs -f rackula-api
To roll back, pin a specific previous release tag in docker-compose.yml instead of latest, and bring the stack back up, the data in the data directory stays untouched, the diagram stays right where it was.
FAQ Schema.org markup (editor’s reference)
In addition to the HowTo markup at the top of the article, it’s recommended to add FAQPage markup for the FAQ block above, kept separate to avoid overloading the main JSON-LD.
Оставайтесь на связи
Рецепты от IT-боли. Без воды, без рекламы, без маркетинговой шелухи.
Подписаться на IT-Аптеку →


