This week, the AI space saw a surge in open-source enthusiasm, with new tools and libraries capturing developer attention. While the headlines focus on stars and strategic partnerships, we’re diving into the nitty-gritty of how we’ve built a self-hosted Postiz instance using Docker Compose and connected it with a Python client that communicates via the /api/public/v1/posts endpoint.
At Apex Grid Technologies, we’ve been evaluating lightweight, self-hosted social platforms as part of our broader exploration into on-device AI integration. Postiz stood out for its minimalism and open API, which made it a natural fit for our experimentation. We chose to self-host it using Docker Compose to ensure we had full control over the environment and could easily scale or modify it as needed.
Setting up Postiz with Docker Compose was straightforward. We used the official Docker image and configured it with our desired settings, such as database connections and authentication options. Here’s a simplified version of our docker-compose.yml:
version: '3.8'
services:
postiz:
image: postiz/postiz:latest
ports:
- "8080:8080"
environment:
POSTIZ_DB_URL: "postgres://user:password@db:5432/postiz"
POSTIZ_JWT_SECRET: "supersecretkey"
depends_on:
- db
db:
image: postgres:15
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: password
POSTGRES_DB: postiz
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
Once the Postiz instance was up and running, we built a Python client to interact with its API. We focused on the /api/public/v1/posts endpoint, which allows for creating and retrieving posts. The client is built using requests, and we handle both the JSON payload and the response structure carefully.
Here’s how we construct the request:
import requests
import json
headers = {
"Authorization": "Bearer <your-jwt-token>",
"Content-Type": "application/json"
}
payload = {
"text": "This is a test post from the Python client.",
"visibility": "public"
}
response = requests.post("http://localhost:8080/api/public/v1/posts", headers=headers, json=payload)
if response.status_code == 200:
result = response.json()
if isinstance(result, list):
print("Received a list of posts:", result)
else:
print("Received a single post:", result)
else:
print("Error:", response.status_code, response.text)
We’ve noticed that the /api/public/v1/posts endpoint can return either a single object or a list depending on the context - for example, when creating a new post, it returns the created object, but when fetching all posts, it returns a list. This is a subtle but important detail for any client consuming the API. We handle this by checking the type of the response using isinstance() and processing accordingly.
One tradeoff we’ve made is the lack of advanced features like rate limiting or more granular permissions, which are not exposed through the public API. However, for our use case, this is acceptable, and we can always extend the client or the Postiz setup if needed.
Looking ahead, we’re exploring ways to integrate this setup with our on-device AI models. The idea is to have a local, self-hosted social layer that can interact with AI-generated content in real time, without relying on external cloud services. We’re also looking into ways to make the Python client more robust, including support for async requests and better error handling.
What are your thoughts on self-hosted social platforms and their potential for AI integration?
For a few weeks I ran a proper multi-agent orchestrator. The concept was right, and I still think the people building those tools are pointed the correct way. But the bill was absurd, and it took me a while to work out why.
It wasn't the coding. It was the talking about the coding.
Every turn, age
You sit down Friday with one messy GitHub issue. You want a usable plan before Monday morning. You paste the text into a chat agent.
The reply looks polished, complete, and very sure. It adds Kafka, Redis, and a new auth service. The issue never named those systems.
You do not have a brownfield bu
Il problema: SQL non e uno standard unico
Sulla carta, SQL e uno standard. Nella pratica, ogni database parla il suo dialetto. MySQL usa i backtick per quotare gli identificatori, PostgreSQL le virgolette doppie, SQLite le accetta entrambe ma preferisce le virgolette. MySQL ha AUTO_INCREMENT, Post