mirror of
https://github.com/Portabase/portabase.git
synced 2026-07-14 11:16:13 +02:00
Compare commits
14
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
98e40466a0 | ||
|
|
324d80a735 | ||
|
|
6770be63b6 | ||
|
|
89aa33ebd8 | ||
|
|
619c32f09d | ||
|
|
c471423775 | ||
|
|
c589b9b8b8 | ||
|
|
bae9ba0973 | ||
|
|
7bfc5c1d9b | ||
|
|
8bd3b0d731 | ||
|
|
06cd4de141 | ||
|
|
33eb2df023 | ||
|
|
540ecd4b60 | ||
|
|
237418f009 |
@@ -7,9 +7,9 @@
|
||||
|
||||
We take security seriously and aim to support the following versions of the project with security updates:
|
||||
|
||||
| Version | Supported |
|
||||
|-----------|--------------------|
|
||||
| 1.x | ✅ Fully Supported |
|
||||
| Version | Supported |
|
||||
|---------|--------------------|
|
||||
| Latest | ✅ Fully Supported |
|
||||
|
||||
---
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
cff-version: 1.2.0
|
||||
title: Portabase
|
||||
message: "If you use this software, please cite it as below."
|
||||
type: software
|
||||
authors:
|
||||
- family-names: Gauthereau
|
||||
given-names: Charles
|
||||
- family-names: Larcher
|
||||
given-names: Killian
|
||||
repository-code: https://github.com/Portabase/portabase
|
||||
url: https://portabase.io
|
||||
abstract: "Portabase is a free, open-source, self-hosted solution for database administration, providing backup and restore capabilities, scheduling, retention policies, notifications, and support for multiple storage backends. Its headless agent architecture enables connection to multiple database instances securely and efficiently."
|
||||
keywords:
|
||||
- docker
|
||||
- kubernetes
|
||||
- backups
|
||||
- postgresql
|
||||
- mysql
|
||||
- mariadb
|
||||
- devops
|
||||
- database
|
||||
- monitoring
|
||||
- s3
|
||||
- self-hosted
|
||||
- system-administration
|
||||
- web-ui
|
||||
- agent
|
||||
license: Apache-2.0
|
||||
version: 1.1.6
|
||||
date-released: "2026-01-01"
|
||||
@@ -6,24 +6,32 @@
|
||||
|
||||
<h3 align="center">Portabase</h3>
|
||||
<p>
|
||||
Free, open-source, and self-hosted solution for automated backup and restoration of your database instances.
|
||||
Take full control of your databases with Portabase — the self-hosted, open-source platform
|
||||
for automated backup, restoration, and operational management. Powered by the <strong>Portabase Agent</strong>,
|
||||
every database in your infrastructure can be monitored, backed up, and managed in real time,
|
||||
with zero reliance on third-party services.
|
||||
</p>
|
||||
<p>
|
||||
Secure, lightweight, and deployable anywhere — on Docker, Kubernetes, or directly on your servers.
|
||||
Designed for teams, DevOps, and enterprises who demand control, reliability, and automation at scale.
|
||||
</p>
|
||||
|
||||
[](LICENSE)
|
||||
[](https://hub.docker.com/r/solucetechnologies/portabase)
|
||||
[](https://github.com/RostislavDugin/postgresus)
|
||||
[](https://github.com/Portabase/portabase)
|
||||
|
||||
[](https://www.postgresql.org/)
|
||||
[](https://www.mysql.com/)
|
||||
[](https://mariadb.org/)
|
||||
[](https://github.com/RostislavDugin/postgresus)
|
||||
[](https://github.com/Portabase/portabase)
|
||||
[](https://github.com/Portabase/portabase)
|
||||
|
||||
|
||||
<p>
|
||||
<strong>
|
||||
<a href="https://portabase.io">Documentation</a> •
|
||||
<a href="https://www.youtube.com/watch?v=hvLbX5LN1UE">Demo</a> •
|
||||
<a href="#installation">Installation</a> •
|
||||
<a href="#contributing">Contributing</a> •
|
||||
<a href="https://github.com/Portabase/portabase/issues/new?labels=bug&template=bug-report---.md">Report Bug</a> •
|
||||
<a href="https://github.com/Portabase/portabase/issues/new?labels=enhancement&template=feature-request---.md">Request Feature</a>
|
||||
</strong>
|
||||
@@ -34,29 +42,17 @@
|
||||
|
||||
</div>
|
||||
|
||||
## 📚 Table of Contents
|
||||
|
||||
- [About The Project](#-about-the-project)
|
||||
- [Getting Started](#-getting-started)
|
||||
- [Usage](#-usage)
|
||||
- [Roadmap](#-roadmap)
|
||||
- [Contributing](#-contributing)
|
||||
- [License](#-license)
|
||||
- [Contact](#-contact)
|
||||
- [Acknowledgments](#-acknowledgments)
|
||||
|
||||
---
|
||||
|
||||
## ✨ About The Project
|
||||
|
||||
**Portabase** is a server dashboard tool designed to simplify the backup and restoration of your database instances. It
|
||||
integrates seamlessly with Portabase agents for managing operations securely and efficiently.
|
||||
|
||||
GitHub Repository: [Portabase](https://github.com/Portabase/portabase)
|
||||
integrates seamlessly with [Portabase agents](https://github.com/Portabase/agent-portabase) for managing operations
|
||||
securely and efficiently.
|
||||
|
||||
### 🔧 Built With
|
||||
|
||||
- [![NextJS][NextJS]][NextJS-url] (v16 with App Router)
|
||||
- [![NextJS][NextJS]][NextJS-url]
|
||||
- [![Drizzle][Drizzle]][Drizzle-url]
|
||||
- [![ShadcnUI][ShadcnUI]][ShadcnUI-url]
|
||||
- [![BetterAuth][BetterAuth]][BetterAuth-url]
|
||||
@@ -64,13 +60,67 @@ GitHub Repository: [Portabase](https://github.com/Portabase/portabase)
|
||||
|
||||
---
|
||||
|
||||
## 📦 Features
|
||||
|
||||
### 🗄️ Supported databases
|
||||
|
||||
- PostgreSQL
|
||||
- MySQL
|
||||
- MariaDB
|
||||
|
||||
### ⏱️ Scheduled backups
|
||||
|
||||
- Cron-based scheduling for full control
|
||||
- Manual trigger support for on-demand backups
|
||||
|
||||
### 💾 Storage backends
|
||||
|
||||
- On-premise storage: Backups are stored directly on your VPS or server
|
||||
- Cloud storage: S3-compatible backends
|
||||
supported ([documentation](https://portabase.io/docs/dashboard/advanced/storage/s3))
|
||||
- Full data ownership: No third-party access — your data stays under your control
|
||||
|
||||
### 🔔 Smart notifications
|
||||
|
||||
- Multi-channel delivery: Email, Slack, Discord, webhooks
|
||||
- Real-time alerts: Immediate feedback on success and failure
|
||||
- Custom alert policies: Database-level notification rules
|
||||
- Team-ready: Designed for DevOps, on-call, and incident workflows
|
||||
|
||||
### 👥 Built for team environments
|
||||
|
||||
- Workspaces: Organize databases, notification channels, and storage backends by organization and project
|
||||
- Access control: Fine-grained, role-based permissions on all resources
|
||||
- Role management: Member, admin, and owner roles at both system and organization levels
|
||||
|
||||
### 🐳 Self-hosted & secure
|
||||
|
||||
- Containerized deployment: Docker-based setup for predictable installation and operations
|
||||
- Privacy by design: All data remains within your own infrastructure
|
||||
- Open source: Apache 2.0 licensed — fully auditable codebase
|
||||
|
||||
### 🤖 Portabase Agent ([details](https://github.com/Portabase/agent-portabase))
|
||||
|
||||
- Headless architecture: Runs locally on your infrastructure to manage backups and database operations
|
||||
- Multi-target support: Single agent can connect to multiple databases across different servers
|
||||
- Lightweight & efficient: Minimal resource footprint while providing full operational control
|
||||
- Secure communication: Encrypted channels between agent and central dashboard
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Getting Started
|
||||
|
||||
### Installation
|
||||
|
||||
You have 3 ways to install **Portabase**:
|
||||
|
||||
- Automated CLI (recommended) - [details](https://portabase.io/docs/cli)
|
||||
- Docker Compose setup - [details](https://portabase.io/docs/dashboard/setup)
|
||||
- Kubernetes with Helm (soon)
|
||||
|
||||
Ensure Docker is installed on your machine before getting started.
|
||||
|
||||
### Option 1: Docker Compose Setup
|
||||
### Docker Compose Setup
|
||||
|
||||
Create a `docker-compose.yml` file with the following configuration:
|
||||
|
||||
@@ -124,93 +174,7 @@ docker compose up -d
|
||||
If you use reverse proxy like
|
||||
Traefik : [Check this link](https://portabase.io/docs/dashboard/advanced/reverse-proxy)
|
||||
|
||||
### Option 2: Locally (Development)
|
||||
|
||||
1. Clone the repository:
|
||||
```bash
|
||||
git clone https://github.com/Portabase/portabase
|
||||
cd portabase
|
||||
```
|
||||
2. Start the development environment:
|
||||
```bash
|
||||
docker compose up
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ Usage
|
||||
|
||||
Portabase provides a web dashboard to manage your database instances and backups.
|
||||
It currently supports:
|
||||
|
||||
- **PostgreSQL**
|
||||
- **MySQL**
|
||||
|
||||
### Process
|
||||
|
||||
1. **Access the dashboard** – Open `http://localhost:8887` in your browser.
|
||||
2. **Sign up** – Register the first user, who will automatically have the **Admin** role in the default workspace.
|
||||
3. **Add your first agent** – Follow [this guide](https://github.com/Portabase/agent-portabase) for setup
|
||||
instructions.
|
||||
4. **Create organizations and projects** – Link your databases to projects to enable backups and restores.
|
||||
5. **Configure backup policies** – Define schedules (hourly, daily, weekly, or monthly) and retention rules.
|
||||
6. **Choose a storage provider** – Select where backups will be stored (local, S3, etc.).
|
||||
7. **Save and start** – Portabase validates your configuration and starts automated backups based on your defined
|
||||
policies.
|
||||
|
||||
---
|
||||
|
||||
## 🗺️ Roadmap
|
||||
|
||||
- [ ] Add changelog
|
||||
- [ ] Enhance documentation
|
||||
- [ ] Implement testing procedures
|
||||
- [ ] Include release file
|
||||
- [ ] Improve security
|
||||
- [ ] Enhance UX/UI
|
||||
- [ ] Improve workspace management system
|
||||
- [x] Migrate to Drizzle ORM
|
||||
- [ ] Add multi-language support
|
||||
- [ ] Extend multi-database support:
|
||||
- [x] PostgreSQL
|
||||
- [ ] MongoDB
|
||||
- [x] MySQL
|
||||
- [x] MariaDB
|
||||
|
||||
Check out [open issues](https://github.com/Portabase/portabase/issues) for more.
|
||||
|
||||
---
|
||||
|
||||
## 🤝 Contributing
|
||||
|
||||
Contributions are welcome and appreciated! Here's how to get started:
|
||||
|
||||
1. Fork the repository
|
||||
2. Create a new branch:
|
||||
```bash
|
||||
git checkout -b feature/YourFeature
|
||||
```
|
||||
3. Commit your changes:
|
||||
```bash
|
||||
git commit -m "Add YourFeature"
|
||||
```
|
||||
4. Push to the branch:
|
||||
```bash
|
||||
git push origin feature/YourFeature
|
||||
```
|
||||
5. Open a pull request
|
||||
|
||||
Give the project a ⭐ if you like it!
|
||||
|
||||
### Top Contributors
|
||||
|
||||
[](https://github.com/Portabase/portabase/graphs/contributors)
|
||||
|
||||
---
|
||||
|
||||
## Developer Notes
|
||||
|
||||
### Environment Variables
|
||||
#### Environment Variables
|
||||
|
||||
```yml
|
||||
# Environment
|
||||
@@ -253,19 +217,67 @@ RETENTION_CRON="* * * * *"
|
||||
```
|
||||
|
||||
To get more information about env variables, check
|
||||
that [link](https://portabase.io/docs/portabase/advanced-topics/environment)
|
||||
that [link](https://portabase.io/docs/dashboard/advanced/environment)
|
||||
|
||||
### Semantic Versioning
|
||||
### Locally (Development)
|
||||
|
||||
Use the following format for Docker image versioning:
|
||||
1. Clone the repository:
|
||||
```bash
|
||||
git clone https://github.com/Portabase/portabase
|
||||
cd portabase
|
||||
```
|
||||
2. Start the development database for Portabase service:
|
||||
```bash
|
||||
docker compose up
|
||||
```
|
||||
|
||||
```bash
|
||||
major.minor.patch-rc.release
|
||||
# Example: 1.0.0-rc.1
|
||||
3. Start the Next.js app:
|
||||
```bash
|
||||
make up
|
||||
```
|
||||
|
||||
major.minor.patch-rc.release-tag
|
||||
# Example: 1.0.0-rc.1-dev
|
||||
```
|
||||
---
|
||||
|
||||
## 🛠️ Usage
|
||||
|
||||
Once the installation process is done, follow the steps to configure your instance.
|
||||
|
||||
### Dashboard configuration process
|
||||
|
||||
1. **Access the dashboard** – Open `http://localhost:8887` in your browser.
|
||||
2. **Sign up** – Register the first user, who will automatically have the **Admin** role in the default workspace.
|
||||
3. **Add your first agent** – Follow [this guide](https://github.com/Portabase/agent-portabase) for setup
|
||||
instructions.
|
||||
4. **Create organizations and projects** – Link your databases to projects to enable backups and restores.
|
||||
5. **Configure backup policies** – Define schedules (hourly, daily, weekly, or monthly) and retention rules.
|
||||
6. **Choose a storage provider** – Select where backups will be stored (local, S3, etc.).
|
||||
7. **Save and start** – Portabase validates your configuration and starts automated backups based on your defined
|
||||
policies.
|
||||
|
||||
---
|
||||
|
||||
## 🤝 Contributing
|
||||
|
||||
Contributions are welcome and appreciated! Here's how to get started:
|
||||
|
||||
1. Fork the repository
|
||||
2. Create a new branch:
|
||||
```bash
|
||||
git checkout -b features/your-feature
|
||||
```
|
||||
3. Commit your changes:
|
||||
```bash
|
||||
git commit -m "Add YourFeature"
|
||||
```
|
||||
4. Push to the branch:
|
||||
```bash
|
||||
git push origin features/your-feature
|
||||
```
|
||||
5. Open a pull request
|
||||
|
||||
### Top Contributors
|
||||
|
||||
[](https://github.com/Portabase/portabase/graphs/contributors)
|
||||
|
||||
---
|
||||
|
||||
@@ -275,18 +287,13 @@ Distributed under the Apache License. See `LICENSE.txt` for more details.
|
||||
|
||||
---
|
||||
|
||||
## 📬 Contact
|
||||
|
||||
- Killian Larcher - killian.larcher@soluce-technologies.com
|
||||
- Charles Gauthereau - charles.gauthereau@soluce-technologies.com
|
||||
- Project Link: [Portabase GitHub](https://github.com/Portabase/portabase)
|
||||
|
||||
---
|
||||
|
||||
## 🙏 Acknowledgments
|
||||
|
||||
Thanks to all contributors and the open-source community!
|
||||
|
||||
Give the project a ⭐ if you like it!
|
||||
|
||||
|
||||
[Docker]: https://img.shields.io/badge/Docker-2496ED?logo=docker&logoColor=fff&style=for-the-badge
|
||||
|
||||
[NextJS]: https://img.shields.io/badge/next.js-000000?style=for-the-badge&logo=nextdotjs&logoColor=white
|
||||
|
||||
@@ -7,7 +7,7 @@ import {Page, PageActions, PageContent, PageHeader, PageTitle} from "@/features/
|
||||
import {notFound} from "next/navigation";
|
||||
import {db} from "@/db";
|
||||
import * as drizzleDb from "@/db";
|
||||
import {eq, not} from "drizzle-orm";
|
||||
import {desc, eq, not} from "drizzle-orm";
|
||||
import {EmptyStatePlaceholder} from "@/components/wrappers/common/empty-state-placeholder";
|
||||
import {Metadata} from "next";
|
||||
|
||||
@@ -21,7 +21,8 @@ export default async function RoutePage(props: PageParams<{}>) {
|
||||
where: not(eq(drizzleDb.schemas.agent.isArchived, true)),
|
||||
with: {
|
||||
databases: true
|
||||
}
|
||||
},
|
||||
orderBy: (fields) => desc(fields.createdAt),
|
||||
});
|
||||
|
||||
console.log(agents);
|
||||
|
||||
@@ -106,7 +106,7 @@ export async function POST(
|
||||
|
||||
if (status === "success") {
|
||||
const file = formData.get("file") as File | null;
|
||||
|
||||
const extension = formData.get("extension") as string | null;
|
||||
if (!aesKeyHex || !ivHex) {
|
||||
return NextResponse.json({error: "Missing fields"}, {status: 400});
|
||||
}
|
||||
@@ -118,12 +118,13 @@ export async function POST(
|
||||
{status: 400}
|
||||
);
|
||||
}
|
||||
const fileExtension = getFileExtension(database.dbms)
|
||||
const fileSizeBytes = file.size;
|
||||
// const fileExtension = '.' + (file.name.split('.').pop()?.toLowerCase() || '');
|
||||
const fileExtension = extension ? extension : getFileExtension(database.dbms)
|
||||
const decryptedFile = await decryptedDump(file, aesKeyHex, ivHex, fileExtension);
|
||||
const uuid = uuidv4();
|
||||
const fileName = `${uuid}${fileExtension}`;
|
||||
const buffer = Buffer.from(await decryptedFile.arrayBuffer());
|
||||
|
||||
const [settings] = await db.select().from(drizzleDb.schemas.setting).where(eq(drizzleDb.schemas.setting.name, "system")).limit(1);
|
||||
if (!settings) {
|
||||
throw new Error("System settings not found.");
|
||||
@@ -149,6 +150,7 @@ export async function POST(
|
||||
.update(drizzleDb.schemas.backup)
|
||||
.set(withUpdatedAt({
|
||||
file: fileName,
|
||||
fileSize: fileSizeBytes,
|
||||
status: 'success',
|
||||
}))
|
||||
.where(eq(drizzleDb.schemas.backup.id, backup.id));
|
||||
|
||||
+3
-1
@@ -43,8 +43,10 @@ const nextConfig: NextConfig = {
|
||||
},
|
||||
experimental: {
|
||||
serverActions: {
|
||||
bodySizeLimit: "50mb",
|
||||
bodySizeLimit: "10gb",
|
||||
},
|
||||
proxyClientMaxBodySize: '10gb',
|
||||
|
||||
},
|
||||
async headers() {
|
||||
return [
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "portabase",
|
||||
"version": "1.1.6",
|
||||
"version": "1.1.8",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev --turbopack -p 8887",
|
||||
|
||||
@@ -42,6 +42,7 @@ export const uploadBackupAction = userAction
|
||||
|
||||
const arrayBuffer = await file.arrayBuffer();
|
||||
|
||||
const fileSize = file.size;
|
||||
const uuid = uuidv4();
|
||||
const fileName = `imported_${uuid}${fileExtension}`;
|
||||
const buffer = Buffer.from(arrayBuffer);
|
||||
@@ -85,6 +86,7 @@ export const uploadBackupAction = userAction
|
||||
status: 'success',
|
||||
databaseId: database.id,
|
||||
file: fileName,
|
||||
fileSize: fileSize,
|
||||
})
|
||||
.returning();
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE "backups" ADD COLUMN "file_size" integer;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -113,6 +113,13 @@
|
||||
"when": 1766426190521,
|
||||
"tag": "0015_absurd_next_avengers",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 16,
|
||||
"version": "7",
|
||||
"when": 1767363779637,
|
||||
"tag": "0016_broken_morgan_stark",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -38,6 +38,7 @@ export const backup = pgTable(
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
status: statusEnum("status").default("waiting").notNull(),
|
||||
file: text("file"),
|
||||
fileSize: integer("file_size"),
|
||||
databaseId: uuid("database_id")
|
||||
.notNull()
|
||||
.references(() => database.id, {onDelete: "cascade"}),
|
||||
|
||||
@@ -31,7 +31,7 @@ import {cn} from "@/lib/utils";
|
||||
import {Tooltip, TooltipContent, TooltipProvider, TooltipTrigger} from "@/components/ui/tooltip";
|
||||
import {MemberWithUser} from "@/db/schema/03_organization";
|
||||
import {formatLocalizedDate} from "@/utils/date-formatting";
|
||||
import {isImportedFilename} from "@/utils/text";
|
||||
import {formatBytes, isImportedFilename} from "@/utils/text";
|
||||
|
||||
|
||||
export function backupColumns(
|
||||
@@ -73,6 +73,14 @@ export function backupColumns(
|
||||
return isImported ? `${reference} (imported)` : `${reference}`
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "fileSize",
|
||||
header: "Size",
|
||||
cell: ({row}) => {
|
||||
console.log(row.original.fileSize)
|
||||
return formatBytes(row.getValue("fileSize"))
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "createdAt",
|
||||
header: "Created At",
|
||||
|
||||
@@ -15,4 +15,14 @@ export function isUUID(str: string) {
|
||||
|
||||
export function isImportedFilename(name: string): boolean {
|
||||
return name.startsWith("imported_");
|
||||
}
|
||||
|
||||
export function formatBytes(bytes: number | null, decimals = 2): string {
|
||||
if (!bytes) return "N/A";
|
||||
if (bytes === 0) return "0 Bytes";
|
||||
const k = 1024;
|
||||
const dm = decimals < 0 ? 0 : decimals;
|
||||
const sizes = ["Bytes", "KB", "MB", "GB", "TB"];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i];
|
||||
}
|
||||
Reference in New Issue
Block a user