Your server. Your listings.
A 24-hour library.
This package is a complete self-hosted application with a private administrator dashboard, two source collectors, a scheduler, local SQLite storage and an optional public board. It is not deployed to your Oracle account yet.
What is included
| Feature | Behaviour |
|---|---|
| Sources | Indians in Kuwait classifieds and 1kuwaitjobs.com. Job discovery uses the site’s RSS feed when available, with accessible-page fallback. |
| Scheduling | 10, 15 or 30 minutes. Initially paused until you enable it. Checks do not overlap. |
| Expiry | 24 hours after first server collection. Duplicates and publishing do not renew expiry. |
| Privacy | All collected records start private. Public board is disabled initially. |
| Publishing | Individual review with a recorded advertiser authorisation or source licence. Enabling the board alone does not publish private records. |
| Storage | SQLite in a persistent Docker volume. Photo links only; no image-file copies. |
1. Confirm an eligible Oracle VM
In Oracle Cloud Console, check Compute → Instances → Create instance. Use your home region and resources explicitly marked Always Free eligible. This package contains no Oracle provisioning API, account-upgrade action, paid API key or paid hosting dependency.
As checked on 3 November 2026, Oracle documents an A1 allowance of 2 OCPUs and 12 GB memory total for an Always Free tenancy. A modest starting configuration for this application is 1 A1 OCPU and 4 GB RAM, within your tenancy’s remaining allowance, with Ubuntu 24.04 and a boot volume covered by your unused free storage. The browser collector is not intended for a 1 GB micro VM.
Verify the pricing labels, remaining boot/block-volume allocation and other resource usage in your own console before creating anything. Free capacity may be unavailable. Oracle may reclaim idle free instances; the app cannot guarantee uninterrupted hosting. Do not upgrade the account or provision a paid substitute if free capacity is unavailable.
Official references: Always Free resources and Oracle Free Tier FAQ.
2. Prepare the VM
- Create an Ubuntu VM only after confirming its free eligibility. Keep its SSH private key on your own computer.
- Connect to the VM over SSH. Install Docker Engine and the Docker Compose plugin using Docker’s official Ubuntu instructions. Check
sudo docker versionandsudo docker compose version. - Copy this ZIP to the VM, using an SSH client or SCP. Replace the example key path and IP below with your actual values; do not paste the private key into the application or a chat.
scp -i /path/to/your-key Kuwait-Ads-Web-App.zip ubuntu@YOUR_VM_IP:~/ ssh -i /path/to/your-key ubuntu@YOUR_VM_IP sudo apt-get update sudo apt-get install -y unzip unzip Kuwait-Ads-Web-App.zip cd kuwait-ads-web
3. Build and create your administrator
sudo bash deploy/start.sh
The script verifies Docker/Compose, builds the app image, asks for an administrator username and a password of at least 12 characters, and starts the app. Password input is hidden and only a salted hash is stored. There is no shared/default login password. The first image build downloads Chromium and Python dependencies and can take several minutes.
The app restarts after a container failure or VM reboot when Docker is running. It listens on VM loopback port 8000 by default. No cloud resource is created by the startup script. The application is one process; do not increase Uvicorn workers or run multiple replicas against the same SQLite file.
4. Open it privately first
On your own computer, open a terminal and create an SSH tunnel:
ssh -N -L 8000:127.0.0.1:8000 -i /path/to/your-key ubuntu@YOUR_VM_IP
Keep that terminal open and visit http://localhost:8000 in your browser. Sign in using the administrator you just created. Port 8000 does not need to be opened in the Oracle network rules.
Your computer only needs the tunnel to view the app. Once scheduling is enabled, the Oracle VM continues collection even when your computer and the tunnel are closed.
5. Start the schedule
- Open Sources & schedule.
- Choose IIK, 1 Kuwait Jobs, or both. Start with 1–2 IIK pages and 10–30 new ads per source.
- Choose Every 30 minutes initially. Check Run collection automatically while the server is online, then Save schedule.
- Use Collect now for an immediate check. Watch Collection history for success, partial, paused or error results.
A source that requires verification, refuses access, rate-limits the browser, or disallows the path in robots.txt is disabled for review. The app does not bypass those controls. A partial or failed run is not a claim that all ads were collected. The job feed is a bounded selection, not an archive of every job.
6. Optional: HTTPS and phone access
Use a subdomain of a domain you already control. Point its DNS A record to your VM’s public IP. Configure Oracle ingress and the VM firewall to allow TCP 80 and 443. Keep SSH restricted to your trusted addresses. These infrastructure/DNS changes are manual; the package does not change them for you.
Edit .env on the VM:
PUBLIC_URL=https://ads.your-domain.com COOKIE_SECURE=true SITE_ADDRESS=ads.your-domain.com
Replace the domain in all three lines, then start the included Caddy HTTPS proxy:
sudo docker compose --profile https up -d
Caddy requests an HTTPS certificate for the configured domain. Open that exact HTTPS URL and sign in. Do not expose the private dashboard over plain public HTTP or open port 8000 to the Internet. If login reports an origin error, make PUBLIC_URL match the browser’s exact scheme and hostname.
7. Publishing on your application
The public page is /board. It starts empty and disabled. To use it, enable the public board in Sources & schedule. Select a listing in the private library, record its publishing permission and authorisation details, select Publish on the public board, then Save review.
Collection automatically adds records to the private dashboard. It does not automatically publish unknown-permission listings. Both private and public versions expire on the original 24-hour clock. Public responses exclude private notes, permission notes, email/phone fields and source identifiers; approved descriptions may themselves contain contact text.
Retention details
At its expiry time a record is excluded from every listing, detail and export API, including the public board. Database deletion runs within the next 30-second maintenance tick while the app is running, and at startup. Source links, descriptions, contacts, photos links, notes and approvals are removed together. Publishing and repeated collection do not extend the clock.
A keyed, non-readable source fingerprint with its original first-seen time remains to stop old ads returning as new. It contains no listing title, URL, description, image or contact. Run summaries retain source name, timing, aggregate counts and generic status for seven days. No raw pages, browser screenshots or listing data are written to run logs.
If the VM is powered off, physical cleanup cannot run until it restarts. On restart cleanup happens before serving the app, and expired records remain inaccessible. Exported files, external backups, snapshots and visitors’ saved copies are outside the app’s deletion control. If strict 24-hour storage is required, do not keep independent snapshots/backups containing expired listing data.
Limits that keep resource use bounded
One browser at a time, up to 5 IIK result pages, 100 new ads per source per run, 5,000 active records, 250,000 dedup fingerprints, 30 photo links per record and 40,000 characters per text field. Requests are limited to 1 MB; source HTML/feed requests to 2 MB. A full dedup store pauses new inserts rather than forgetting expired IDs. Browser image/font/media downloads are blocked where possible. The app does not perform OCR on phone-number images, open application forms, send messages or reveal hidden contact details.
The jobs collector uses up to the first 200 feed entries. If RSS is unavailable, it falls back to visible job links on the homepage. Collection uses the standard browser engine without stealth, proxy rotation or challenge-solving. Selectors were inspected on the live sites; source layout changes can still require maintenance.
Maintenance
# Container status and resource use sudo docker compose ps sudo docker stats --no-stream # Bounded service logs sudo docker compose logs --tail=100 app # Reset administrator password; also revokes earlier sessions sudo docker compose run --rm app python -m app.admin # Update after replacing source files sudo docker compose up -d --build app # Stop services without deleting persistent data sudo docker compose --profile https down
Do not add -v to the down command unless you intentionally want to delete stored data and the administrator account. Keep the data volume private. The session cookie is HTTP-only, same-site restricted and secure in HTTPS mode; updates require a CSRF token. Authentication failures are rate-limited.
What has been verified
Automated backend tests cover authentication, CSRF, logged-out session revocation, publishing checks, expiry without cleanup, duplicate expiry, dedup after deletion, RSS parsing, source URL restrictions and settings limits. The dashboard layout was reviewed in a browser using read-only synthetic data. Docker/Oracle deployment and uninterrupted live scheduled collection have not been tested on your account. No actual source records or credentials are included in this download.