diff --git a/src/config/load.ts b/src/config/load.ts
index 5bb01f3d41..29e4a66f91 100644
--- a/src/config/load.ts
+++ b/src/config/load.ts
@@ -4,9 +4,10 @@
 
 import * as fs from 'fs';
 import { URL } from 'url';
+import $ from 'cafy';
 import * as yaml from 'js-yaml';
-import { Source, Mixin } from './types';
 import * as pkg from '../../package.json';
+import { fromNullable } from '../prelude/maybe';
 
 /**
  * Path of configuration directory
@@ -21,30 +22,148 @@ const path = process.env.NODE_ENV == 'test'
 	: `${dir}/default.yml`;
 
 export default function load() {
-	const config = yaml.safeLoad(fs.readFileSync(path, 'utf-8')) as Source;
+	const config = yaml.safeLoad(fs.readFileSync(path, 'utf-8'));
 
-	const mixin = {} as Mixin;
+	if (typeof config.url !== 'string') {
+		throw 'You need to configure the URL.';
+	}
 
 	const url = validateUrl(config.url);
-	config.url = normalizeUrl(config.url);
 
-	mixin.host = url.host;
-	mixin.hostname = url.hostname;
-	mixin.scheme = url.protocol.replace(/:$/, '');
-	mixin.ws_scheme = mixin.scheme.replace('http', 'ws');
-	mixin.ws_url = `${mixin.ws_scheme}://${mixin.host}`;
-	mixin.api_url = `${mixin.scheme}://${mixin.host}/api`;
-	mixin.auth_url = `${mixin.scheme}://${mixin.host}/auth`;
-	mixin.dev_url = `${mixin.scheme}://${mixin.host}/dev`;
-	mixin.docs_url = `${mixin.scheme}://${mixin.host}/docs`;
-	mixin.stats_url = `${mixin.scheme}://${mixin.host}/stats`;
-	mixin.status_url = `${mixin.scheme}://${mixin.host}/status`;
-	mixin.drive_url = `${mixin.scheme}://${mixin.host}/files`;
-	mixin.user_agent = `Misskey/${pkg.version} (${config.url})`;
+	if (typeof config.port !== 'number') {
+		throw 'You need to configure the port.';
+	}
 
-	if (config.autoAdmin == null) config.autoAdmin = false;
+	if (config.https != null) {
+		if (typeof config.https.key !== 'string') {
+			throw 'You need to configure the https key.';
+		}
+		if (typeof config.https.cert !== 'string') {
+			throw 'You need to configure the https cert.';
+		}
+	}
 
-	return Object.assign(config, mixin);
+	if (config.mongodb == null) {
+		throw 'You need to configure the MongoDB.';
+	}
+
+	if (typeof config.mongodb.host !== 'string') {
+		throw 'You need to configure the MongoDB host.';
+	}
+
+	if (typeof config.mongodb.port !== 'number') {
+		throw 'You need to configure the MongoDB port.';
+	}
+
+	if (typeof config.mongodb.db !== 'string') {
+		throw 'You need to configure the MongoDB database name.';
+	}
+
+	if (config.drive == null) {
+		throw 'You need to configure the drive.';
+	}
+
+	if (typeof config.drive.storage !== 'string') {
+		throw 'You need to configure the drive storage type.';
+	}
+
+	if (!$.str.or(['db', 'minio']).ok(config.drive.storage)) {
+		throw 'Unrecognized drive storage type is specified.';
+	}
+
+	if (config.drive.storage === 'minio') {
+		if (typeof config.drive.storage.bucket !== 'string') {
+			throw 'You need to configure the minio bucket.';
+		}
+
+		if (typeof config.drive.storage.prefix !== 'string') {
+			throw 'You need to configure the minio prefix.';
+		}
+
+		if (config.drive.storage.prefix.config == null) {
+			throw 'You need to configure the minio.';
+		}
+	}
+
+	if (config.redis != null) {
+		if (typeof config.redis.host !== 'string') {
+			throw 'You need to configure the Redis host.';
+		}
+
+		if (typeof config.redis.port !== 'number') {
+			throw 'You need to configure the Redis port.';
+		}
+	}
+
+	if (config.elasticsearch != null) {
+		if (typeof config.elasticsearch.host !== 'string') {
+			throw 'You need to configure the Elasticsearch host.';
+		}
+
+		if (typeof config.elasticsearch.port !== 'number') {
+			throw 'You need to configure the Elasticsearch port.';
+		}
+	}
+
+	const source = {
+		url: normalizeUrl(config.url as string),
+		port: config.port as number,
+		https: fromNullable(config.https).map(x => ({
+			key: x.key as string,
+			cert: x.cert as string,
+			ca: fromNullable<string>(x.ca)
+		})),
+		mongodb: {
+			host: config.mongodb.host as string,
+			port: config.mongodb.port as number,
+			db: config.mongodb.db as string,
+			user: fromNullable<string>(config.mongodb.user),
+			pass: fromNullable<string>(config.mongodb.pass)
+		},
+		redis: fromNullable(config.redis).map(x => ({
+			host: x.host as string,
+			port: x.port as number,
+			pass: fromNullable<string>(x.pass)
+		})),
+		elasticsearch: fromNullable(config.elasticsearch).map(x => ({
+			host: x.host as string,
+			port: x.port as number,
+			pass: fromNullable<string>(x.pass)
+		})),
+		disableHsts: typeof config.disableHsts === 'boolean' ? config.disableHsts as boolean : false,
+		drive: {
+			storage: config.drive.storage as string,
+			bucket: config.drive.bucket as string,
+			prefix: config.drive.prefix as string,
+			baseUrl: fromNullable<string>(config.drive.baseUrl),
+			config: config.drive.config
+		},
+		autoAdmin: typeof config.autoAdmin === 'boolean' ? config.autoAdmin as boolean : false,
+		proxy: fromNullable<string>(config.proxy),
+		clusterLimit: typeof config.clusterLimit === 'number' ? config.clusterLimit as number : Infinity,
+	};
+
+	const host = url.host;
+	const scheme = url.protocol.replace(/:$/, '');
+	const ws_scheme = scheme.replace('http', 'ws');
+
+	const mixin = {
+		host: url.host,
+		hostname: url.hostname,
+		scheme: scheme,
+		ws_scheme: ws_scheme,
+		ws_url: `${ws_scheme}://${host}`,
+		api_url: `${scheme}://${host}/api`,
+		auth_url: `${scheme}://${host}/auth`,
+		dev_url: `${scheme}://${host}/dev`,
+		docs_url: `${scheme}://${host}/docs`,
+		stats_url: `${scheme}://${host}/stats`,
+		status_url: `${scheme}://${host}/status`,
+		drive_url: `${scheme}://${host}/files`,
+		user_agent: `Misskey/${pkg.version} (${config.url})`
+	};
+
+	return Object.assign(source, mixin);
 }
 
 function tryCreateUrl(url: string) {
diff --git a/src/config/types.ts b/src/config/types.ts
index 2ce9c0c80d..b133e64c25 100644
--- a/src/config/types.ts
+++ b/src/config/types.ts
@@ -1,64 +1,3 @@
-/**
- * ユーザーが設定する必要のある情報
- */
-export type Source = {
-	repository_url?: string;
-	feedback_url?: string;
-	url: string;
-	port: number;
-	https?: { [x: string]: string };
-	disableHsts?: boolean;
-	mongodb: {
-		host: string;
-		port: number;
-		db: string;
-		user: string;
-		pass: string;
-	};
-	redis: {
-		host: string;
-		port: number;
-		pass: string;
-	};
-	elasticsearch: {
-		host: string;
-		port: number;
-		pass: string;
-	};
-	drive?: {
-		storage: string;
-		bucket?: string;
-		prefix?: string;
-		baseUrl?: string;
-		config?: any;
-	};
+import load from "./load";
 
-	autoAdmin?: boolean;
-
-	proxy?: string;
-
-	accesslog?: string;
-
-	clusterLimit?: number;
-};
-
-/**
- * Misskeyが自動的に(ユーザーが設定した情報から推論して)設定する情報
- */
-export type Mixin = {
-	host: string;
-	hostname: string;
-	scheme: string;
-	ws_scheme: string;
-	api_url: string;
-	ws_url: string;
-	auth_url: string;
-	docs_url: string;
-	stats_url: string;
-	status_url: string;
-	dev_url: string;
-	drive_url: string;
-	user_agent: string;
-};
-
-export type Config = Source & Mixin;
+export type Config = ReturnType<typeof load>;
diff --git a/src/db/elasticsearch.ts b/src/db/elasticsearch.ts
index cbe6afbbb9..68ad736b25 100644
--- a/src/db/elasticsearch.ts
+++ b/src/db/elasticsearch.ts
@@ -42,9 +42,10 @@ const index = {
 };
 
 // Init ElasticSearch connection
-const client = config.elasticsearch ? new elasticsearch.Client({
-	host: `${config.elasticsearch.host}:${config.elasticsearch.port}`
-}) : null;
+
+const client = config.elasticsearch.map(({ host, port }) => {
+	return new elasticsearch.Client({ host: `${host}:${port}` });
+}).getOrElse(null);
 
 if (client) {
 	// Send a HEAD request
diff --git a/src/db/mongodb.ts b/src/db/mongodb.ts
index dedb289ce9..3e7d40fde7 100644
--- a/src/db/mongodb.ts
+++ b/src/db/mongodb.ts
@@ -1,7 +1,7 @@
 import config from '../config';
 
-const u = config.mongodb.user ? encodeURIComponent(config.mongodb.user) : null;
-const p = config.mongodb.pass ? encodeURIComponent(config.mongodb.pass) : null;
+const u = config.mongodb.user.map(x => encodeURIComponent(x)).getOrElse(null);
+const p = config.mongodb.pass.map(x => encodeURIComponent(x)).getOrElse(null);
 
 const uri = `mongodb://${u && p ? `${u}:${p}@` : ''}${config.mongodb.host}:${config.mongodb.port}/${config.mongodb.db}`;
 
diff --git a/src/db/redis.ts b/src/db/redis.ts
index 48e3f4e43e..4193ac7e7b 100644
--- a/src/db/redis.ts
+++ b/src/db/redis.ts
@@ -1,10 +1,8 @@
 import * as redis from 'redis';
 import config from '../config';
 
-export default config.redis ? redis.createClient(
-	config.redis.port,
-	config.redis.host,
-	{
-		auth_pass: config.redis.pass
-	}
-) : null;
+export default config.redis.map(({ host, port, pass }) => {
+	return redis.createClient(port, host, {
+		auth_pass: pass.getOrElse(null)
+	});
+}).getOrElse(null);
diff --git a/src/index.ts b/src/index.ts
index 6983ec722e..13f2d0b7d8 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -228,7 +228,7 @@ async function init(): Promise<Config> {
 	return config;
 }
 
-async function spawnWorkers(limit: number = Infinity) {
+async function spawnWorkers(limit: number) {
 	const workers = Math.min(limit, os.cpus().length);
 	bootLogger.info(`Starting ${workers} worker${workers === 1 ? '' : 's'}...`);
 	await Promise.all([...Array(workers)].map(spawnWorker));
diff --git a/src/prelude/maybe.ts b/src/prelude/maybe.ts
index f9ac95c0b5..857fc80195 100644
--- a/src/prelude/maybe.ts
+++ b/src/prelude/maybe.ts
@@ -1,5 +1,7 @@
 export interface Maybe<T> {
 	isJust(): this is Just<T>;
+	map<S>(f: (x: T) => S): Maybe<S>;
+	getOrElse(x: T): T;
 }
 
 export type Just<T> = Maybe<T> & {
@@ -9,6 +11,8 @@ export type Just<T> = Maybe<T> & {
 export function just<T>(value: T): Just<T> {
 	return {
 		isJust: () => true,
+		getOrElse: (_: T) => value,
+		map: <S>(f: (x: T) => S) => just(f(value)),
 		get: () => value
 	};
 }
@@ -16,5 +20,11 @@ export function just<T>(value: T): Just<T> {
 export function nothing<T>(): Maybe<T> {
 	return {
 		isJust: () => false,
+		getOrElse: (value: T) => value,
+		map: <S>(_: (x: T) => S) => nothing<S>()
 	};
 }
+
+export function fromNullable<T>(value: T): Maybe<T> {
+	return value == null ? nothing() : just(value);
+}
diff --git a/src/queue/index.ts b/src/queue/index.ts
index 5d3baa8243..28768bf38f 100644
--- a/src/queue/index.ts
+++ b/src/queue/index.ts
@@ -8,17 +8,17 @@ import handler from './processors';
 import { queueLogger } from './logger';
 
 const enableQueue = !program.disableQueue;
-const queueAvailable = config.redis != null;
+const queueAvailable = config.redis.isJust();
 
 const queue = initializeQueue();
 
 function initializeQueue() {
-	if (queueAvailable) {
+	return config.redis.map(({ port, host, pass }) => {
 		return new Queue('misskey', {
 			redis: {
-				port: config.redis.port,
-				host: config.redis.host,
-				password: config.redis.pass
+				port: port,
+				host: host,
+				password: pass.getOrElse(null)
 			},
 
 			removeOnSuccess: true,
@@ -27,9 +27,7 @@ function initializeQueue() {
 			sendEvents: false,
 			storeJobs: false
 		});
-	} else {
-		return null;
-	}
+	}).getOrElse(null);
 }
 
 export function deliver(user: ILocalUser, content: any, to: any) {
diff --git a/src/remote/activitypub/resolver.ts b/src/remote/activitypub/resolver.ts
index 049e645e47..cae37cacd4 100644
--- a/src/remote/activitypub/resolver.ts
+++ b/src/remote/activitypub/resolver.ts
@@ -57,7 +57,7 @@ export default class Resolver {
 
 		const object = await request({
 			url: value,
-			proxy: config.proxy,
+			proxy: config.proxy.getOrElse(null),
 			timeout: this.timeout,
 			headers: {
 				'User-Agent': config.user_agent,
diff --git a/src/server/api/endpoints/meta.ts b/src/server/api/endpoints/meta.ts
index 91cb095c92..d08d0b0734 100644
--- a/src/server/api/endpoints/meta.ts
+++ b/src/server/api/endpoints/meta.ts
@@ -46,7 +46,7 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => {
 		description: instance.description,
 		langs: instance.langs,
 
-		secure: config.https != null,
+		secure: config.https.isJust(),
 		machine: os.hostname(),
 		os: os.platform(),
 		node: process.version,
@@ -83,9 +83,9 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => {
 			registration: !instance.disableRegistration,
 			localTimeLine: !instance.disableLocalTimeline,
 			globalTimeLine: !instance.disableGlobalTimeline,
-			elasticsearch: config.elasticsearch ? true : false,
+			elasticsearch: config.elasticsearch.isJust(),
 			recaptcha: instance.enableRecaptcha,
-			objectStorage: config.drive && config.drive.storage === 'minio',
+			objectStorage: config.drive.storage === 'minio',
 			twitter: instance.enableTwitterIntegration,
 			github: instance.enableGithubIntegration,
 			discord: instance.enableDiscordIntegration,
diff --git a/src/server/api/endpoints/users/recommendation.ts b/src/server/api/endpoints/users/recommendation.ts
index e3a03888b6..d07c4b08f0 100644
--- a/src/server/api/endpoints/users/recommendation.ts
+++ b/src/server/api/endpoints/users/recommendation.ts
@@ -50,7 +50,7 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => {
 
 		request({
 			url: url,
-			proxy: config.proxy,
+			proxy: config.proxy.getOrElse(null),
 			timeout: timeout,
 			json: true,
 			followRedirect: true,
diff --git a/src/server/api/streaming.ts b/src/server/api/streaming.ts
index f8f3c0ff4a..d70cec03dc 100644
--- a/src/server/api/streaming.ts
+++ b/src/server/api/streaming.ts
@@ -23,10 +23,10 @@ module.exports = (server: http.Server) => {
 
 		let ev: EventEmitter;
 
-		if (config.redis) {
+		if (config.redis.isJust()) {
 			// Connect to Redis
 			const subscriber = redis.createClient(
-				config.redis.port, config.redis.host);
+				config.redis.get().port, config.redis.get().host);
 
 			subscriber.subscribe('misskey');
 
diff --git a/src/server/index.ts b/src/server/index.ts
index 0e1c701050..df252a3575 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -96,13 +96,14 @@ app.use(router.routes());
 app.use(mount(require('./web')));
 
 function createServer() {
-	if (config.https) {
-		const certs: any = {};
-		for (const k of Object.keys(config.https)) {
-			certs[k] = fs.readFileSync(config.https[k]);
-		}
-		certs['allowHTTP1'] = true;
-		return http2.createSecureServer(certs, app.callback()) as https.Server;
+	if (config.https.isJust()) {
+		const opts = {
+			key: fs.readFileSync(config.https.get().key),
+			cert: fs.readFileSync(config.https.get().cert),
+			...config.https.get().ca.map<any>(path => ({ ca: fs.readFileSync(path) })).getOrElse({}),
+			allowHTTP1: true
+		};
+		return http2.createSecureServer(opts, app.callback()) as https.Server;
 	} else {
 		return http.createServer(app.callback());
 	}
diff --git a/src/server/proxy/proxy-media.ts b/src/server/proxy/proxy-media.ts
index 3f234a727d..95491b6141 100644
--- a/src/server/proxy/proxy-media.ts
+++ b/src/server/proxy/proxy-media.ts
@@ -69,7 +69,7 @@ async function fetch(url: string, path: string) {
 
 		const req = request({
 			url: requestUrl,
-			proxy: config.proxy,
+			proxy: config.proxy.getOrElse(null),
 			timeout: 10 * 1000,
 			headers: {
 				'User-Agent': config.user_agent
diff --git a/src/server/web/index.ts b/src/server/web/index.ts
index 3f2e1ed19f..d8a8d75c7a 100644
--- a/src/server/web/index.ts
+++ b/src/server/web/index.ts
@@ -31,7 +31,9 @@ const app = new Koa();
 app.use(views(__dirname + '/views', {
 	extension: 'pug',
 	options: {
-		config
+		config: {
+			url: config.url
+		}
 	}
 }));
 
diff --git a/src/services/drive/add-file.ts b/src/services/drive/add-file.ts
index 31902b2425..b19794b8e2 100644
--- a/src/services/drive/add-file.ts
+++ b/src/services/drive/add-file.ts
@@ -50,8 +50,9 @@ async function save(path: string, name: string, type: string, hash: string, size
 			if (type === 'image/webp') ext = '.webp';
 		}
 
-		const baseUrl = config.drive.baseUrl
-			|| `${ config.drive.config.useSSL ? 'https' : 'http' }://${ config.drive.config.endPoint }${ config.drive.config.port ? `:${config.drive.config.port}` : '' }/${ config.drive.bucket }`;
+		const baseUrl = config.drive.baseUrl.getOrElse(
+			`${ config.drive.config.useSSL ? 'https' : 'http' }://${ config.drive.config.endPoint }${ config.drive.config.port ? `:${config.drive.config.port}` : '' }/${ config.drive.bucket }`
+		);
 
 		// for original
 		const key = `${config.drive.prefix}/${uuid.v4()}${ext}`;
diff --git a/src/services/drive/upload-from-url.ts b/src/services/drive/upload-from-url.ts
index 8daecbadbc..c5c9763d5d 100644
--- a/src/services/drive/upload-from-url.ts
+++ b/src/services/drive/upload-from-url.ts
@@ -55,7 +55,7 @@ export default async (
 
 		const req = request({
 			url: requestUrl,
-			proxy: config.proxy,
+			proxy: config.proxy.getOrElse(null),
 			timeout: 10 * 1000,
 			headers: {
 				'User-Agent': config.user_agent
diff --git a/src/services/note/create.ts b/src/services/note/create.ts
index 9ccf3be9e8..6afdcbbfbd 100644
--- a/src/services/note/create.ts
+++ b/src/services/note/create.ts
@@ -500,7 +500,7 @@ async function insertNote(user: IUser, data: Option, tags: string[], emojis: str
 }
 
 function index(note: INote) {
-	if (note.text == null || config.elasticsearch == null) return;
+	if (note.text == null || !config.elasticsearch.isJust()) return;
 
 	es.index({
 		index: 'misskey',
diff --git a/src/tools/move-drive-files.ts b/src/tools/move-drive-files.ts
index 8a1e944503..263e5e125d 100644
--- a/src/tools/move-drive-files.ts
+++ b/src/tools/move-drive-files.ts
@@ -37,8 +37,9 @@ async function job(file: IDriveFile): Promise<any> {
 	const thumbnailKeyDir = `${config.drive.prefix}/${uuid.v4()}`;
 	const thumbnailKey = `${thumbnailKeyDir}/${name}.thumbnail.jpg`;
 
-	const baseUrl = config.drive.baseUrl
-		|| `${ config.drive.config.useSSL ? 'https' : 'http' }://${ config.drive.config.endPoint }${ config.drive.config.port ? `:${config.drive.config.port}` : '' }/${ config.drive.bucket }`;
+	const baseUrl = config.drive.baseUrl.getOrElse(
+		`${ config.drive.config.useSSL ? 'https' : 'http' }://${ config.drive.config.endPoint }${ config.drive.config.port ? `:${config.drive.config.port}` : '' }/${ config.drive.bucket }`
+	);
 
 	const bucket = await getDriveFileBucket();
 	const readable = bucket.openDownloadStream(file._id);