# SINGLETON. Exactly one beat process may ever run against this Redis, or every periodic task is # double-scheduled (duplicate daily ingestion, duplicate discovery scans, duplicate peer-universe # recomputes). systemd enforces this better than compose did: a .service is a single unit and # ContainerName makes Podman itself refuse a second container of the same name on this host. It # does NOT protect against a second beat on a DIFFERENT host pointed at the same Redis — that is # operator discipline, and stocklab-doctor.sh cannot check it either. [Unit] Description=StockLab Celery beat scheduler (singleton) PartOf=stocklab.target Wants=network-online.target After=network-online.target After=stocklab-db.service stocklab-redis.service Requires=stocklab-db.service stocklab-redis.service StartLimitBurst=5 StartLimitIntervalSec=300 [Container] ContainerName=stocklab-beat Image=localhost/stocklab-backend:screener-percent-final-20260909-194639 Network=host EnvironmentFile=/etc/stocklab/stocklab.env Environment=ENVIRONMENT=production Exec=celery -A app.workers.celery_app.celery_app beat --loglevel=info DropCapability=ALL NoNewPrivileges=true [Service] # AUDIT FIX (final master pass, §84 "Не допускай uncontrolled resource growth"). # No container had ANY limit: a runaway worker could take the whole machine, and §81 requires # StockLab not to damage the other services already running on this production host. # These are STARTING POINTS sized for a small single-server deployment, not measurements — nothing # here has been profiled, because nothing here has ever run. Raise them from observed usage rather # than trusting the numbers. # IMPORTANT for rootless: a cgroup v2 host that has not DELEGATED the memory/cpu controllers to the # user slice accepts these silently and ignores them. `./scripts/stocklab-doctor.sh --rootless # check-cgroups` reports which controllers are actually delegated — check it before believing a # limit is in force. MemoryMax=256M MemorySwapMax=0 CPUQuota=25% TasksMax=128 Restart=always # Bounded restart storm: 5 attempts in 5 minutes, then stop trying and stay failed so # `systemctl status stocklab` shows a real failure instead of an endless loop. [Install] # §78: `systemctl start stocklab` starts this container, and # `systemctl stop stocklab` stops it. WantedBy pulls it in with the target; # PartOf propagates stop/restart down from the target. WantedBy=multi-user.target stocklab.target