diff --git a/apps/docs/docs/modules/storage.md b/apps/docs/docs/modules/storage.md
index bec0cf65a3aaeaba0cb3e8b038e5defb3e46466e..526d216eccc127abe48c7312f93fd71d3e2fa9a7 100644
--- a/apps/docs/docs/modules/storage.md
+++ b/apps/docs/docs/modules/storage.md
@@ -4,12 +4,19 @@ sidebar_position: 7
 
 # Storage
 
-Storage in LlamaIndex.TS works automatically once you've configured a `StorageContext` object. Just configure the `persistDir` and attach it to an index.
+Storage in LlamaIndex.TS works automatically once you've configured a
+`StorageContext` object.
 
-Right now, only saving and loading from disk is supported, with future integrations planned!
+## Local Storage
+
+You can configure the `persistDir` and attach it to an index.
 
 ```typescript
-import { Document, VectorStoreIndex, storageContextFromDefaults } from "./src";
+import {
+  Document,
+  VectorStoreIndex,
+  storageContextFromDefaults,
+} from "llamaindex";
 
 const storageContext = await storageContextFromDefaults({
   persistDir: "./storage",
@@ -21,6 +28,33 @@ const index = await VectorStoreIndex.fromDocuments([document], {
 });
 ```
 
+## PostgreSQL Storage
+
+You can configure the `schemaName`, `tableName`, `namespace`, and
+`connectionString`. If a `connectionString` is not
+provided, it will use the environment variables `PGHOST`, `PGUSER`,
+`PGPASSWORD`, `PGDATABASE` and `PGPORT`.
+
+```typescript
+import {
+  Document,
+  VectorStoreIndex,
+  PostgresDocumentStore,
+  PostgresIndexStore,
+  storageContextFromDefaults,
+} from "llamaindex";
+
+const storageContext = await storageContextFromDefaults({
+  docStore: new PostgresDocumentStore(),
+  indexStore: new PostgresIndexStore(),
+});
+
+const document = new Document({ text: "Test Text" });
+const index = await VectorStoreIndex.fromDocuments([document], {
+  storageContext,
+});
+```
+
 ## API Reference
 
 - [StorageContext](../api/interfaces/StorageContext.md)
diff --git a/packages/llamaindex/src/storage/docStore/PostgresDocumentStore.ts b/packages/llamaindex/src/storage/docStore/PostgresDocumentStore.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b61f15d90d3abef9e649aa4db105150a43a43f0b
--- /dev/null
+++ b/packages/llamaindex/src/storage/docStore/PostgresDocumentStore.ts
@@ -0,0 +1,21 @@
+import { DEFAULT_NAMESPACE } from "@llamaindex/core/global";
+import { PostgresKVStore } from "../kvStore/PostgresKVStore.js";
+import { KVDocumentStore } from "./KVDocumentStore.js";
+
+const DEFAULT_TABLE_NAME = "llamaindex_doc_store";
+
+export class PostgresDocumentStore extends KVDocumentStore {
+  constructor(config?: {
+    schemaName?: string;
+    tableName?: string;
+    connectionString?: string;
+    namespace?: string;
+  }) {
+    const kvStore = new PostgresKVStore({
+      schemaName: config?.schemaName,
+      tableName: config?.tableName || DEFAULT_TABLE_NAME,
+    });
+    const namespace = config?.namespace || DEFAULT_NAMESPACE;
+    super(kvStore, namespace);
+  }
+}
diff --git a/packages/llamaindex/src/storage/index.ts b/packages/llamaindex/src/storage/index.ts
index 5bf08e026a45e38de15902e524c078e7f4c250ce..ecb182ebc7eae9970b510d68933673dc4b10bf0f 100644
--- a/packages/llamaindex/src/storage/index.ts
+++ b/packages/llamaindex/src/storage/index.ts
@@ -1,10 +1,13 @@
 export { SimpleChatStore } from "./chatStore/SimpleChatStore.js";
 export * from "./chatStore/types.js";
+export { PostgresDocumentStore } from "./docStore/PostgresDocumentStore.js";
 export { SimpleDocumentStore } from "./docStore/SimpleDocumentStore.js";
 export * from "./docStore/types.js";
 export * from "./FileSystem.js";
+export { PostgresIndexStore } from "./indexStore/PostgresIndexStore.js";
 export { SimpleIndexStore } from "./indexStore/SimpleIndexStore.js";
 export * from "./indexStore/types.js";
+export { PostgresKVStore } from "./kvStore/PostgresKVStore.js";
 export { SimpleKVStore } from "./kvStore/SimpleKVStore.js";
 export * from "./kvStore/types.js";
 export * from "./StorageContext.js";
diff --git a/packages/llamaindex/src/storage/indexStore/PostgresIndexStore.ts b/packages/llamaindex/src/storage/indexStore/PostgresIndexStore.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ee7991816b1eb8e21433c3a74010e11b1d6c10b0
--- /dev/null
+++ b/packages/llamaindex/src/storage/indexStore/PostgresIndexStore.ts
@@ -0,0 +1,21 @@
+import { DEFAULT_NAMESPACE } from "@llamaindex/core/global";
+import { PostgresKVStore } from "../kvStore/PostgresKVStore.js";
+import { KVIndexStore } from "./KVIndexStore.js";
+
+const DEFAULT_TABLE_NAME = "llamaindex_index_store";
+
+export class PostgresIndexStore extends KVIndexStore {
+  constructor(config?: {
+    schemaName?: string;
+    tableName?: string;
+    connectionString?: string;
+    namespace?: string;
+  }) {
+    const kvStore = new PostgresKVStore({
+      schemaName: config?.schemaName,
+      tableName: config?.tableName || DEFAULT_TABLE_NAME,
+    });
+    const namespace = config?.namespace || DEFAULT_NAMESPACE;
+    super(kvStore, namespace);
+  }
+}
diff --git a/packages/llamaindex/src/storage/kvStore/PostgresKVStore.ts b/packages/llamaindex/src/storage/kvStore/PostgresKVStore.ts
new file mode 100644
index 0000000000000000000000000000000000000000..cadcb06b78ef1ce81576c48aea8f4641ff0217a5
--- /dev/null
+++ b/packages/llamaindex/src/storage/kvStore/PostgresKVStore.ts
@@ -0,0 +1,140 @@
+import { DEFAULT_COLLECTION } from "@llamaindex/core/global";
+import type pg from "pg";
+import { BaseKVStore } from "./types.js";
+
+export type DataType = Record<string, Record<string, any>>;
+
+const DEFAULT_SCHEMA_NAME = "public";
+const DEFAULT_TABLE_NAME = "llamaindex_kv_store";
+
+export class PostgresKVStore extends BaseKVStore {
+  private schemaName: string;
+  private tableName: string;
+  private connectionString: string | undefined = undefined;
+  private db?: pg.Client;
+
+  constructor(config?: {
+    schemaName?: string | undefined;
+    tableName?: string | undefined;
+    connectionString?: string | undefined;
+  }) {
+    super();
+    this.schemaName = config?.schemaName || DEFAULT_SCHEMA_NAME;
+    this.tableName = config?.tableName || DEFAULT_TABLE_NAME;
+    this.connectionString = config?.connectionString;
+  }
+
+  private async getDb(): Promise<pg.Client> {
+    if (!this.db) {
+      try {
+        const pg = await import("pg");
+        const { Client } = pg.default ? pg.default : pg;
+        const db = new Client({ connectionString: this.connectionString });
+        await db.connect();
+        await this.checkSchema(db);
+        this.db = db;
+      } catch (err) {
+        console.error(err);
+        return Promise.reject(err instanceof Error ? err : new Error(`${err}`));
+      }
+    }
+    return Promise.resolve(this.db);
+  }
+
+  private async checkSchema(db: pg.Client) {
+    await db.query(`CREATE SCHEMA IF NOT EXISTS ${this.schemaName}`);
+    const tbl = `CREATE TABLE IF NOT EXISTS ${this.schemaName}.${this.tableName} (
+      id uuid DEFAULT gen_random_uuid() PRIMARY KEY,
+      collection VARCHAR,
+      key VARCHAR,
+      value JSONB DEFAULT '{}'
+    )`;
+    await db.query(tbl);
+    const idxs = `CREATE INDEX IF NOT EXISTS idx_${this.tableName}_collection ON ${this.schemaName}.${this.tableName} (collection);
+      CREATE INDEX IF NOT EXISTS idx_${this.tableName}_key ON ${this.schemaName}.${this.tableName} (key);`;
+    await db.query(idxs);
+    return db;
+  }
+
+  client() {
+    return this.getDb();
+  }
+
+  async put(
+    key: string,
+    val: any,
+    collection: string = DEFAULT_COLLECTION,
+  ): Promise<void> {
+    const db = await this.getDb();
+    try {
+      await db.query("BEGIN");
+      const sql = `
+        INSERT INTO ${this.schemaName}.${this.tableName}
+          (collection, key, value)
+        VALUES ($1, $2, $3)
+        ON CONFLICT (id) DO UPDATE SET
+          collection = EXCLUDED.collection,
+          key = EXCLUDED.key,
+          value = EXCLUDED.value
+        RETURNING id
+      `;
+      const values = [collection, key, val];
+      await db.query(sql, values);
+      await db.query("COMMIT");
+    } catch (error) {
+      await db.query("ROLLBACK");
+      throw error;
+    }
+  }
+
+  async get(
+    key: string,
+    collection: string = DEFAULT_COLLECTION,
+  ): Promise<any> {
+    const db = await this.getDb();
+    try {
+      await db.query("BEGIN");
+      const sql = `SELECT * FROM ${this.schemaName}.${this.tableName} WHERE key = $1 AND collection = $2`;
+      const result = await db.query(sql, [key, collection]);
+      await db.query("COMMIT");
+      return result.rows[0].value;
+    } catch (error) {
+      await db.query("ROLLBACK");
+      throw error;
+    }
+  }
+
+  async getAll(collection: string = DEFAULT_COLLECTION): Promise<DataType> {
+    const db = await this.getDb();
+    try {
+      await db.query("BEGIN");
+      const sql = `SELECT * FROM ${this.schemaName}.${this.tableName} WHERE collection = $1`;
+      const result = await db.query(sql, [collection]);
+      await db.query("COMMIT");
+      return result.rows.reduce((acc, row) => {
+        acc[row.key] = row.value;
+        return acc;
+      }, {});
+    } catch (error) {
+      await db.query("ROLLBACK");
+      throw error;
+    }
+  }
+
+  async delete(
+    key: string,
+    collection: string = DEFAULT_COLLECTION,
+  ): Promise<boolean> {
+    const db = await this.getDb();
+    try {
+      await db.query("BEGIN");
+      const sql = `DELETE FROM ${this.schemaName}.${this.tableName} WHERE key = $1 AND collection = $2`;
+      const result = await db.query(sql, [key, collection]);
+      await db.query("COMMIT");
+      return !!result.rowCount && result.rowCount > 0;
+    } catch (error) {
+      await db.query("ROLLBACK");
+      throw error;
+    }
+  }
+}