Short version for readers who want the point first
Classic BI systems answer questions that were defined in advance. Big Data warehouses make sense when scale, distribution and data volume really demand them. In many companies the problem is simpler: the data is already in SQL, but few people can query it quickly without technical support.
The project described here takes a different route. Instead of building a data warehouse first, it creates a private conversation layer over an existing Microsoft SQL Server database. The user asks in OpenWebUI, Dify runs the agent, the agent uses an SQL tool, and the database stays in a private network accessible through Tailscale.
This case study is not about “magic artificial intelligence”. It is about a very practical setup: controlled data access, a described database schema, limited permissions, query logging and a conversational interface that lets business users ask questions without knowing SQL.
| Area | Description |
|---|---|
| Problem | The data is in the database, but access to knowledge requires an analyst, a report or SQL skills. |
| Solution | The AI agent receives controlled access to an SQL Query Tool and generates queries based on user questions. |
| Architecture | Hetzner, Tailscale, Coolify, Dify, OpenWebUI, MSSQL and a private Docker network instead of exposing the database publicly. |
Problem: the company has data, but no conversation with it
In theory, company data is organized. There are tables, keys, relationships, statuses, dates, contractors, invoices, products, orders and change history. In practice, for many business users an SQL database is a black box. The knowledge exists, but access to it goes through a bottleneck: an analyst, a developer, a scheduled report or a BI panel that has to be designed first.
That means a simple question can turn into a process. Someone wants to know which products are selling worse than last month. Someone else asks which customers have a growing churn risk. Management needs an answer here and now, but the reporting system only shows what was prepared earlier.
This is exactly where the gap between data and decision appears. SQL is excellent for technical users, but it is not the natural working language of a salesperson, operations manager or business owner. BI is convenient when questions repeat, but it is weaker at exploration: “check this variant too”, “what if we exclude cancelled orders”, “show only premium-segment customers”.
Why you do not always need to start with Big Data or classic BI
Big Data and BI are useful, but they are not the answer to every case. If a company stores data in a relational database and the main questions involve filtering, aggregation, comparisons, trends and record retrieval, the first problem is usually not the lack of a warehouse. The problem is the lack of a simple way to ask questions.
Classic BI works well where metrics, KPIs and dashboards are repeatable. An AI agent adds something different: flexibility. The user does not need to know which table contains a column or how customers are joined with transactions. They can start with intent.
| Approach | Best for | Limitation |
|---|---|---|
| BI reports | Repeatable dashboards, KPIs, controlling and management reporting. | Questions must be anticipated and views must be prepared in advance. |
| Big Data | Very large volumes, many sources, event streams and analytics at scale. | High implementation, maintenance and data-modeling cost. |
| AI + SQL | Fast ad hoc questions, data exploration, operational work and analyst support. | Requires a good schema description, permission control and query validation. |
Technical goal of the project
The goal was not to build another dashboard. The goal was to launch a private environment where a non-technical person can ask a question about data, and an AI agent can translate that intent into a safe query against an SQL database.
In practice, the project had to meet several conditions at once: the database must not be public, the admin panel must not hang on the internet, the user should work through a simple chat interface, and the agent should operate in tool mode instead of guessing an answer from model memory.
What had to work after deployment
- Access to the environment only through Tailscale.
- Coolify as the container management panel.
- Dify running in agent mode with tools enabled.
- Microsoft SQL Server available in a private Docker network.
- OpenWebUI connected to Dify through an OpenAI-compatible endpoint.
What we intentionally avoided
- Public exposure of the MSSQL port.
- A public Coolify panel after bootstrap.
- Manual SQL writing by the business user.
- Building a data warehouse before validating pilot value.
- Pretending that the model knows the data without a real database connection.
Architecture: a private system for talking to an SQL database
The design assumes that the whole setup runs on a Hetzner server with Ubuntu 24. The public internet is needed only during bootstrap. The target access path to panels, applications and services goes through Tailscale, a private network based on WireGuard. This matters because a database should not be exposed publicly just because an AI agent needs to use it.
Coolify acts as the container manager. Dify, OpenWebUI, Microsoft SQL Server, Redis, PostgreSQL, Qdrant and the plugin daemon run in one private Docker network. The important point is that Dify connects to MSSQL by container name in internal DNS, not through a public IP address.
The simplest description of the architecture is this: the user enters OpenWebUI through Tailscale, OpenWebUI talks to Dify, Dify runs the agent and calls the SQL tool, and the SQL tool queries MSSQL inside the private container network. The answer returns along the same path, but the user sees it as a normal conversation.
Component roles: what each layer does
In a project like this it is easy to throw several technology names into one bucket, even though each one is responsible for something different. Technically, each layer has a separate function, and only the combination creates a useful system.
| Component | Role in the system | Why it matters |
|---|---|---|
| Hetzner VPS | The machine running the container environment. | Provides infrastructure control without building a large cloud platform. |
| Tailscale | Private network for access to panels and services. | Reduces public exposure and allows ports to be closed after bootstrap. |
| Coolify | Panel for managing containers and services. | Simplifies maintenance of Dify, OpenWebUI, MSSQL and supporting services. |
| Dify | Agent orchestration layer for prompts and tools. | This is where the model receives the SQL tool and database rules. |
| OpenWebUI | Chat interface for the end user. | Hides technical complexity and allows natural-language work. |
| Microsoft SQL Server | Source of business data. | The system works on existing data, not manually prepared exports. |
What was implemented: from server to agent
1. Hetzner and basic server hardening
The first stage was system updates, a minimal firewall and VPS preparation. Public SSH is needed only at the beginning. After enabling Tailscale it should be closed, and incoming traffic should be limited to the private network interface. This turns the server from a classic public admin panel into a private node in the tailnet.
2. Tailscale as the only access gateway
Tailscale changes the character of the deployment. Instead of exposing Coolify, OpenWebUI, Dify and MSSQL to the internet, the system is available only to devices inside the tailnet. It is a simple decision that reduces the attack surface and the number of operational problems.
3. Coolify as the container panel
Coolify makes it possible to manage Docker applications without manually maintaining the full command stack. After installation, it should also not remain public on the administration port. Access through the Tailscale address is enough.
4. Microsoft SQL Server as the source of truth
The MSSQL database stores the data the agent queries. It is important not to map port 1433 publicly unless there is a real need. Dify should see the database through the private container network. From a security perspective, the agent should use a separate technical user with limited permissions.
5. Dify in agent mode
Dify is responsible for the agent logic: it accepts the user intent, selects a tool, builds the query, interprets the result and returns an answer that a human can understand. This is where the main value appears: the agent can use an SQL tool, but the user does not need to write SQL.
Dify is also the place where system instructions, limits and the data schema description can be added.
6. OpenWebUI as the business front end
OpenWebUI connects to Dify through an OpenAI V1-compatible endpoint. For the end user it looks like a conversation with an assistant. Under the hood, however, the assistant has access to a tool that lets it query a specific database.
How the agent understands the database if it does not know it in advance
A language model should not guess the database structure. If the user asks about “active customers”, the agent must know whether activity means a login, an order, a payment, a sales contact or no overdue balance. This is not a model problem. It is a data dictionary problem.
That is why an AI + SQL implementation should include a context layer: descriptions of tables, relationships, columns, statuses, filters and metric definitions. Without it, the agent may generate SQL that is syntactically correct but wrong from a business perspective.
Minimum context for the agent
- List of tables that can be queried.
- Description of the meaning of key columns.
- Relationships between customers, orders, invoices and products.
- Definitions of business terms: active customer, churn, delay, margin.
- Rules for filtering test, cancelled and archived data.
Example business rule
Active customer means a customer who placed at least one non-cancelled order in the last 90 days or has an open subscription contract. The mere existence of a record in the customer table does not mean activity.
Such a definition should be included in the agent instructions, because without it the model may interpret “active” too casually.
What a data question looks like in practice
The user does not ask the database: SELECT ... GROUP BY .... The user asks the way they would ask an analyst:
- “Which products had the largest sales drop in the last 30 days?”
- “Show customers who have not ordered for 60 days but were active before.”
- “Compare this week’s revenue with the previous week and point out the biggest differences.”
- “Find overdue invoices and group them by salesperson.”
The agent turns the question into a plan: it recognizes entities, selects tables, creates a query, executes it through the SQL tool and then describes the result. A good implementation does not stop at “here is the table”. It should also explain how it understands the result, what matters most and where follow-up questions are useful.
Example answer: “The largest drop appears in three categories. Two of them have fewer orders but a similar average basket value. The third has both fewer orders and a lower transaction value, so it needs separate analysis.”
Flow of one question
The question is natural, not technical: “show customers whose sales dropped the most compared with the previous month”.
The agent recognizes that it needs transaction data, a date range and a period comparison.
It uses table descriptions, relationships and metric definitions instead of guessing column names.
The query should be limited, readable and compliant with security rules.
The result may be a table, aggregation, ranking or list of records for further analysis.
The answer should include conclusions, caveats and suggested follow-up questions.
End-to-end example: from question to answer
The easiest way to evaluate this kind of system is to follow one concrete run. The example below is simplified and uses fictional table names, but it shows the correct mechanism: the user asks a business question, the agent builds a controlled query, and the answer comes back in a human-readable form.
User question
“Show the 5 products with the largest sales drop in the last 30 days compared with the previous 30 days. Exclude cancelled orders.”
Example SQL generated by the agent
WITH sales AS (
SELECT
p.product_name,
SUM(CASE
WHEN o.order_date >= DATEADD(DAY, -30, CAST(GETDATE() AS date))
THEN oi.quantity * oi.unit_price ELSE 0
END) AS current_period,
SUM(CASE
WHEN o.order_date >= DATEADD(DAY, -60, CAST(GETDATE() AS date))
AND o.order_date < DATEADD(DAY, -30, CAST(GETDATE() AS date))
THEN oi.quantity * oi.unit_price ELSE 0
END) AS previous_period
FROM orders o
JOIN order_items oi ON oi.order_id = o.id
JOIN products p ON p.id = oi.product_id
WHERE o.status <> 'cancelled'
AND o.order_date >= DATEADD(DAY, -60, CAST(GETDATE() AS date))
GROUP BY p.product_name
)
SELECT TOP (5)
product_name,
current_period,
previous_period,
current_period - previous_period AS change_value
FROM sales
WHERE previous_period > 0
ORDER BY change_value ASC;
Answer visible to the user
The agent should not finish with the table alone. A good answer says what was calculated, which filters were applied and where it is worth asking more. For example: “The largest sales drop is visible in products A, B and C. The result compares the last 30 days with the previous 30 days and excludes cancelled orders. In two products the number of transactions dropped, and in one product the average order value dropped, so it is worth checking stock availability and salesperson activity separately.”
Guardrails: what must be refined for this to be useful
Connecting a model to a database is not enough. The biggest difference between a prototype and a production tool lies in limits, context and validation. The AI agent must have freedom to ask questions about data, but it cannot have freedom to do everything.
| Area | What must be defined | Why it matters |
|---|---|---|
| Permissions | Whether the agent is read-only, which tables it sees and whether it can run expensive queries. | The model should not have broader rights than the real business user. |
| Schema description | Table names, relationships, column meanings, status dictionaries and metric definitions. | Without this, the agent may build a technically correct but business-wrong result. |
| Limits | Maximum record count, timeouts and a ban on full scans without filters. | Protects the database from accidental overload. |
| Audit | Log of the question, generated SQL, execution time and answer. | Allows checking what the agent did and improving the rules. |
| Prompt injection | User instructions and data returned from the database must not overwrite system rules. | Protects against commands hidden in a record or in the question. |
| Data scope | Allowlist of tables, columns and views, with sensitive data blocked outside the pilot scope. | Reduces the risk of accidental disclosure of data the user should not see. |
The safest start is read-only mode, a limited table scope and a set of test questions. Only after passing tests should permissions, data sources and automations be expanded.
- Separate database user for the agent.
- Access only to tables needed in the pilot.
- Read-only access by default.
- Limit on the number of returned records.
- Timeout for computationally expensive queries.
- Logging of the user question and generated SQL.
- Blocking of data-modifying operations.
- Allowlist of tables and columns available to the agent.
- Rejection of instructions that try to change system rules or bypass limits.
- Test-question list compared with manual SQL results.
Security: why the private network is part of the product
In this project, security is not an add-on at the end. It is part of the architecture. Public ports for Coolify, OpenWebUI, Dify and MSSQL should be closed after deployment, and access should go through Tailscale. This is especially important in a system that connects an AI model with client data.
CrowdSec can make sense as additional log monitoring and brute-force detection, but if traffic is not public, its role is supportive. The biggest benefit comes from reducing exposure: fewer public services, fewer entry points and fewer things to watch.
In a classic deployment, an application panel often sits behind a public reverse proxy, the database has a mapped port, and protection relies on passwords and firewall configuration. Here the priority is different: administrative services and the database should be invisible from the public internet. Tailscale becomes the gateway, not an add-on.
| Element | Publicly | Target state |
|---|---|---|
| Coolify | Needed during installation or configuration. | Access only through Tailscale. |
| MSSQL | Should not be exposed publicly. | Access only inside the private container network. |
| OpenWebUI | Does not need to be public in the pilot. | Access for users from the tailnet. |
| SSH | Bootstrap only. | Administration through the private Tailscale address. |
Acceptance tests: how we know the system works
In a technical case study it is worth clearly separating “we installed the containers” from “the system meets the goal”. A working deployment does not only mean green statuses in Docker. It means the full flow from user to database and back is controlled, repeatable and verifiable.
- The user connects to the tailnet and sees the private environment.
- Coolify is available through a Tailscale address, not a public port.
- OpenWebUI connects to Dify through an OpenAI-compatible endpoint.
- Dify runs in agent mode and has the SQL tool configured.
- Dify sees MSSQL by container name or private network, not by public address.
- The agent answers a set of test questions consistently with manually checked SQL.
- The database is not accessible from the public internet.
- The logs can reconstruct the user question, SQL query and answer.
The most important test is simple: a non-technical person asks a business question, the system executes the correct database query, and the answer is understandable without reading SQL. If this works on real questions, the pilot makes sense.
State after deployment: what the company actually gets
After a correct launch, the user connects through Tailscale, enters OpenWebUI and talks to the agent. Through Dify, the agent uses the SQL tool and queries Microsoft SQL Server inside the private container network. This is not yet a full enterprise analytics platform, but it is a very practical bridge between the database and everyday business questions.
The greatest value appears in three places:
- Answer speed. An ad hoc question does not have to wait for a new dashboard.
- Data availability. Non-technical people can explore information on their own.
- Lower entry barrier. The company can validate the value of AI over data before investing in heavier BI or Big Data architecture.
Limitations: what this system does not promise
A good technical case study should also show the boundaries of the solution. AI + SQL does not make data automatically correct, consistent or well described. If the database contains wrong statuses, unclear definitions or historical exceptions, the agent can only inherit them.
| Risk | What can go wrong | How to reduce the problem |
|---|---|---|
| Unclear question | The user asks for “best customers” without defining the criterion. | The agent should ask a follow-up question or use a documented business definition. |
| Weak schema description | The model selects a table with a similar name but a different meaning. | Prepare a dictionary of tables, columns and relationships. |
| Expensive query | The agent generates a broad scan of a large table. | Use limits, timeouts, date ranges and default result constraints. |
| Excessive trust | The user treats the answer as an audited financial report. | Clearly mark the exploratory mode and validate key decisions. |
FAQ: common questions about AI + SQL
Does the AI agent replace the data analyst?
No. A well-implemented agent takes over some repeatable questions and exploration, but an analyst is still needed for data modeling, metric definitions, quality control and interpretation of more difficult results.
Does the user really not need to know SQL?
The user does not need SQL syntax, but the system must know the database structure. That is the difference. The user asks in natural language, while the agent uses the schema description and SQL tool to do the technical work underneath.
Is this an alternative to BI?
It is an alternative for some BI use cases, especially where ad hoc questions and quick situational awareness matter. It does not replace stable, audited financial dashboards, but it can reduce the number of reports created only to answer one-off questions.
Is this suitable for client data?
Yes, provided the deployment is private, permissions are limited, access is controlled and queries are logged. In the described architecture, Tailscale and the lack of public MSSQL exposure play an important role.
Should the agent have access to the whole database?
Not at the start. The most reasonable approach is to begin with a limited table scope and a read-only user. Only after validating test questions should the visible data scope be expanded.
Can agent answers be treated like a financial report?
At the pilot stage, usually not. This is an exploratory and operational layer. Financial reporting, compliance and high-risk decisions should have separate validation, versioned metric definitions and human control.
Where should the first test start?
Start with one database, one process and a dozen or so real business questions. Then check whether the agent selects the right tables, whether the results match manual SQL and whether the answers are understandable for users.
What next: roadmap after the pilot
The most reasonable next step is a pilot version on real but limited client data. The point is not to build a large data platform immediately. The point is to check whether people in the company can make decisions faster when, instead of requesting a report, they can simply ask the data.
| Step | Area | Description |
|---|---|---|
| 1 | Data in SQL | The client’s existing database is connected as the source of truth, without publicly exposing ports. |
| 2 | Private layer | Tailscale and a private container network limit access to administrative services and data. |
| 3 | Dify agent | The agent receives the schema description, security rules and a tool for controlled SQL queries. |
| 4 | Conversation with data | The user asks in natural language, and the system returns a result with a short business explanation. |
- Prepare a dictionary of tables, columns, relationships and business concepts.
- Select the first process: sales, invoices, warehouse, customer service or operations.
- Collect 20-30 real questions that users ask analysts.
- Compare agent answers with manually prepared SQL queries.
- Add user roles and data access scopes.
- Consider integration with n8n or Make for recurring alerts and automation.
Contact about implementation
The author of this publication is 5c0rp10n from JustOneCore. For consulting, a pilot or launching a similar system, contact: contact@justonecore.pl.
This is where AI + SQL makes the most sense: as a lightweight, secure and practical conversation layer over what the company already has. It does not replace all analytics, but it can radically shorten the path from a question to the first useful answer.