ForumLA didn't start as a custom forum. It started as a WordPress website running two well-known plugins — BuddyPress for the social activity stream and wpForo for the community forum. That combination was the standard answer for anyone building a social community site on WordPress at the time, and for a while it worked well enough. But "well enough" has a way of becoming a ceiling.
This thread is the story of why we walked away from both, built our own stream from scratch, and eventually replaced wpForo with a fully custom PHP forum engine. It's a technical journey, but it's also a story about what happens when you stop accepting limitations and start building exactly what you need.
The WordPress Era: BuddyPress + wpForo
ForumLA.us spent several years as a WordPress installation. The core setup was:
- BuddyPress — handling the social activity stream, member profiles, and social interactions
- wpForo — a full-featured forum plugin sitting at
/community, handling categories, topics, posts, and user reputation - The rest of the site running standard WordPress with a Kadence theme
The forum side of this actually worked reasonably well. wpForo is a capable plugin — clean category management, proper thread/reply structure, decent moderation tools. Over roughly five years of use it accumulated a substantial library of technical posts: drone builds, 3D printing troubleshooting, battery chemistry, networking configurations, server setups, and hundreds of personal build logs that documented the learning process behind this entire infrastructure.
The problem was the activity stream.
Why BuddyPress Had to Go
BuddyPress is one of the oldest social layer plugins for WordPress. It's also one of the heaviest. The fundamental issues that drove us away from it:
Performance overhead. BuddyPress adds a significant query load to every page load, even pages that have nothing to do with the activity stream. It hooks into WordPress at a deep level and that cost is paid whether you're on the stream page or a completely unrelated post.
Rigid activity types. The BuddyPress activity stream is designed around a specific model — WordPress post activity, member joins, friendship actions. Customizing it to show exactly what you want, formatted how you want, requires fighting the plugin's own architecture. Custom activity types exist but they're bolted on, not native.
No real media handling. Embedding YouTube videos, displaying uploaded images in the feed, handling multiple attachments per post — none of this worked the way a modern social feed should. Third-party add-ons existed but added more weight to an already heavy stack.
WordPress dependency. Every BuddyPress stream post lived inside the WordPress ecosystem. It couldn't easily be shared across other properties. It couldn't be exposed as a clean API. It was locked inside a CMS architecture.
The decision point came when we started thinking about building a stream for PetBlip. The idea of deploying BuddyPress on another WordPress install to get a social feed on a pet photo site was immediately rejected. If we were going to build a stream for PetBlip, it needed to be something we controlled completely — something that could be deployed anywhere, styled to fit any site, and maintained as a single codebase.
Building the Custom Stream: Attempt by Attempt
The honest version of this story includes the fact that it took multiple serious attempts before the stream that runs today was finished. This wasn't a single clean build — it was an iterative process that ran into real problems along the way.
Attempt 1 focused on database design. The first version had a solid SQL schema — act_posts, act_likes, act_attachments, user tables, hashtag tables. Clean relational structure with proper indexing. The frontend was designed with a modern card-based feed, a Quill rich text editor for post composition, and drag-and-drop file uploads. The database work was solid. The connection layer between that database and the frontend kept breaking down.
Attempt 2 rebuilt the frontend around Twitter-style post composition: text posts, embedded YouTube videos, uploaded images, multi-file attachments. This version got much further — the post display was working, the feed was loading — but complex interactions like multi-image posts and nested comment threads kept hitting edge cases that crashed the thread before they could be resolved.
The version that shipped took the lessons from both previous attempts and built on a simpler, more maintainable architecture. PHP backend with clean API endpoints, a single-page feed that loads posts chronologically, image uploads routing through the vc1 media server, and no dependency on WordPress for anything. The stream uses its own user authentication, its own database tables (prefixed fla_), and its own admin tools.
That stream now runs at stream.forumla.us and is deployed as the same codebase on PetBlip.com, where it powers the main activity feed for the pet photo community. One codebase, two deployments, no WordPress.
The wpForo Problem
With the stream solved, attention turned back to the forum. wpForo was still running, still holding five-plus years of technical posts. But the WordPress install it lived on had accumulated years of clutter — theme experiments, abandoned plugins, database overhead from multiple site rebuilds. The install had been migrated from HostArmada to the local ser2 server, and that migration itself caused wpForo to fail entirely — the plugin's custom database tables didn't transfer cleanly, and the forum at /community went down.
That failure was actually clarifying. Rather than spending time debugging a broken WordPress plugin migration, the question became: why are we running WordPress at all?
The honest answer was: we were running WordPress because wpForo required it. The stream was now independent. The rest of the site didn't need a CMS. The only thing keeping WordPress in the stack was the forum plugin.
The wpForo content itself — the actual threads and posts — was valuable. Five years of personal technical documentation. Build logs for the Frankenrouter, notes from drone builds, 3D printing troubleshooting sequences, networking configurations. The kind of knowledge that's hard to reconstruct because it was written in the moment, during the actual problem-solving process. That content needed to be preserved.
But the decision was made not to migrate it into another forum platform. Instead: build the forum from scratch, then manually curate and rebuild the threads that matter.
Building the Custom Forum Engine
The new ForumLA forum is a fully custom PHP/MySQL application running on ser2 under HestiaCP. No WordPress. No forum framework. Just a clean schema and PHP written to do exactly what a forum needs to do.
Database schema (S2_forumla2 on MariaDB):
fla_users— members with roles (admin, moderator, member), post counts, avatar URLsfla_sessions— server-side session management, no WordPress auth dependencyfla_categories— top-level groupings with icons and sort orderfla_forums— boards within categories, thread/post counters, last activity trackingfla_threads— individual discussion threads with pin/lock/AI flags, view counts, slug-based URLsfla_posts— individual posts with bothbody_htmlandbody_rawstored separatelyfla_media— image attachments routed through the vc1 media server atvideo.localad.ccfla_ai_log— logging for AI-assisted post generationfla_migration— slug redirect table for preserving old URL paths
Features built from the start:
- Full category and board management with an admin panel
- Thread creation with markdown-style formatting (
**bold**,*italic*,`code`, YouTube embeds) - Image attachments uploaded to vc1 and stored with thumbnail URLs
- Pinned and locked thread flags
- Pagination on both thread lists and post lists
- View counter on each thread
- Admin thread management with pin, lock, delete, and full edit via TinyMCE WYSIWYG
- "New thread" indicators on the forum index — green pulse dot and badge showing how many threads were posted in the last 48 hours
- Recent activity sidebar and most-active thread rankings on the forum index
AI Writing Assistant built into thread creation. The new-thread page has a sidebar panel where you describe what you want to write, choose a tone (technical, conversational, narrative) and length, and the AI generates a full draft — title and body — using either Claude (Anthropic) or the local qwen2.5:32b model running on ai1. The draft populates the form for review and editing before posting. It's a workflow assist, not auto-posting.
Thread Injector — an admin tool at /admin/thread-inject.php that allows creating threads with raw HTML body content, bypassing the standard textarea flow. This is how longer, structured posts are created — content is composed externally (or generated by AI with full context), then injected directly into the database as formatted HTML. The injector also handles category and forum creation, making it the primary tool for rebuilding the wpForo content library thread by thread.
The Content Migration Strategy
The old wpForo database had roughly 158 posts across dozens of categories. Rather than doing a bulk automated migration — which would have imported five years of confused first-draft notes, typos, half-finished troubleshooting threads, and dead ends — the decision was made to curate manually.
Each wpForo thread gets evaluated. If it contains information worth keeping, it gets rebuilt as a clean, properly formatted ForumLA thread using the Thread Injector. The AI Writing Assistant helps restructure the raw content into readable posts. Threads that were just noise get left behind.
This is slower than an automated migration. It's also producing a better result — a knowledge base of threads that are actually readable, properly categorized, and reflect what was actually learned rather than the messy process of learning it.
Where Things Stand
ForumLA today runs entirely on self-hosted infrastructure with no WordPress, no BuddyPress, and no wpForo. The technology stack is:
- ser2 (10.0.20.10) — HestiaCP, nginx + Apache, PHP 8.3-FPM, MariaDB
- vc1 (10.0.1.160) — media server handling image uploads and derivatives
- ai1 (10.0.1.190) — local AI server running qwen2.5:32b via Ollama, FastAPI on port 8001
- Frankenrouter (r2) — HAProxy routing all traffic to the right backend by domain/SNI
The stream at stream.forumla.us runs independently. The forum at forumla.us/forumla is the custom engine described above. Both share the same self-hosted media infrastructure. Neither depends on WordPress for anything.
The wpForo content library is being rebuilt thread by thread into the new forum. That's an ongoing project — there's no deadline on it. The goal is a clean, usable knowledge base, not a speed record.
If you're reading this on ForumLA, you're using the result of that entire journey.